summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar t8952024-01-26 09:06:24 -0500
committerGravatar t8952024-01-27 20:05:51 -0500
commit3f1290cee3e2419956de3f5b31d0446ff5425f4a (patch)
tree75fd375885ee8c55a1b7715f281c985996bf9370 /src
parentMerge pull request #12818 from K900/small-fixes (diff)
downloadyuzu-3f1290cee3e2419956de3f5b31d0446ff5425f4a.tar.gz
yuzu-3f1290cee3e2419956de3f5b31d0446ff5425f4a.tar.xz
yuzu-3f1290cee3e2419956de3f5b31d0446ff5425f4a.zip
android: Multi-program app switching
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt33
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp7
-rw-r--r--src/android/app/src/main/jni/id_cache.h1
-rw-r--r--src/android/app/src/main/jni/native.cpp34
-rw-r--r--src/android/app/src/main/jni/native.h7
8 files changed, 117 insertions, 34 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 55abba093..53137b2e2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -261,7 +261,7 @@ object NativeLibrary {
261 /** 261 /**
262 * Begins emulation. 262 * Begins emulation.
263 */ 263 */
264 external fun run(path: String?) 264 external fun run(path: String?, programIndex: Int = 0)
265 265
266 // Surface Handling 266 // Surface Handling
267 external fun surfaceChanged(surf: Surface?) 267 external fun surfaceChanged(surf: Surface?)
@@ -489,6 +489,12 @@ object NativeLibrary {
489 sEmulationActivity.get()!!.onEmulationStopped(status) 489 sEmulationActivity.get()!!.onEmulationStopped(status)
490 } 490 }
491 491
492 @Keep
493 @JvmStatic
494 fun onProgramChanged(programIndex: Int) {
495 sEmulationActivity.get()!!.onProgramChanged(programIndex)
496 }
497
492 /** 498 /**
493 * Logs the Yuzu version, Android version and, CPU. 499 * Logs the Yuzu version, Android version and, CPU.
494 */ 500 */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 26cddecf4..564aaf305 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -76,7 +76,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
76 76
77 override fun onDestroy() { 77 override fun onDestroy() {
78 stopForegroundService(this) 78 stopForegroundService(this)
79 emulationViewModel.clear()
80 super.onDestroy() 79 super.onDestroy()
81 } 80 }
82 81
@@ -446,9 +445,14 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
446 } 445 }
447 446
448 fun onEmulationStopped(status: Int) { 447 fun onEmulationStopped(status: Int) {
449 if (status == 0) { 448 if (status == 0 && emulationViewModel.programChanged.value == -1) {
450 finish() 449 finish()
451 } 450 }
451 emulationViewModel.setEmulationStopped(true)
452 }
453
454 fun onProgramChanged(programIndex: Int) {
455 emulationViewModel.setProgramChanged(programIndex)
452 } 456 }
453 457
454 private fun startMotionSensorListener() { 458 private fun startMotionSensorListener() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index ef393c4be..1f591ced1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -424,10 +424,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
424 } 424 }
425 } 425 }
426 } 426 }
427 launch {
428 repeatOnLifecycle(Lifecycle.State.CREATED) {
429 emulationViewModel.programChanged.collect {
430 if (it != 0) {
431 emulationViewModel.setEmulationStarted(false)
432 binding.drawerLayout.close()
433 binding.drawerLayout
434 .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
435 ViewUtils.hideView(binding.surfaceInputOverlay)
436 ViewUtils.showView(binding.loadingIndicator)
437 }
438 }
439 }
440 }
441 launch {
442 repeatOnLifecycle(Lifecycle.State.CREATED) {
443 emulationViewModel.emulationStopped.collect {
444 if (it && emulationViewModel.programChanged.value != -1) {
445 if (perfStatsUpdater != null) {
446 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
447 }
448 emulationState.changeProgram(emulationViewModel.programChanged.value)
449 emulationViewModel.setProgramChanged(-1)
450 emulationViewModel.setEmulationStopped(false)
451 }
452 }
453 }
454 }
427 } 455 }
428 } 456 }
429 457
430 private fun startEmulation() { 458 private fun startEmulation(programIndex: Int = 0) {
431 if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) { 459 if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
432 if (!DirectoryInitialization.areDirectoriesReady) { 460 if (!DirectoryInitialization.areDirectoriesReady) {
433 DirectoryInitialization.start() 461 DirectoryInitialization.start()
@@ -435,7 +463,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
435 463
436 updateScreenLayout() 464 updateScreenLayout()
437 465
438 emulationState.run(emulationActivity!!.isActivityRecreated) 466 emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
439 } 467 }
440 } 468 }
441 469
@@ -833,6 +861,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
833 ) { 861 ) {
834 private var state: State 862 private var state: State
835 private var surface: Surface? = null 863 private var surface: Surface? = null
864 lateinit var emulationThread: Thread
836 865
837 init { 866 init {
838 // Starting state is stopped. 867 // Starting state is stopped.
@@ -878,7 +907,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
878 } 907 }
879 908
880 @Synchronized 909 @Synchronized
881 fun run(isActivityRecreated: Boolean) { 910 fun run(isActivityRecreated: Boolean, programIndex: Int = 0) {
882 if (isActivityRecreated) { 911 if (isActivityRecreated) {
883 if (NativeLibrary.isRunning()) { 912 if (NativeLibrary.isRunning()) {
884 state = State.PAUSED 913 state = State.PAUSED
@@ -889,10 +918,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
889 918
890 // If the surface is set, run now. Otherwise, wait for it to get set. 919 // If the surface is set, run now. Otherwise, wait for it to get set.
891 if (surface != null) { 920 if (surface != null) {
892 runWithValidSurface() 921 runWithValidSurface(programIndex)
893 } 922 }
894 } 923 }
895 924
925 @Synchronized
926 fun changeProgram(programIndex: Int) {
927 emulationThread.join()
928 emulationThread = Thread({
929 Log.debug("[EmulationFragment] Starting emulation thread.")
930 NativeLibrary.run(gamePath, programIndex)
931 }, "NativeEmulation")
932 emulationThread.start()
933 }
934
896 // Surface callbacks 935 // Surface callbacks
897 @Synchronized 936 @Synchronized
898 fun newSurface(surface: Surface?) { 937 fun newSurface(surface: Surface?) {
@@ -932,7 +971,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
932 } 971 }
933 } 972 }
934 973
935 private fun runWithValidSurface() { 974 private fun runWithValidSurface(programIndex: Int = 0) {
936 NativeLibrary.surfaceChanged(surface) 975 NativeLibrary.surfaceChanged(surface)
937 if (!emulationCanStart.invoke()) { 976 if (!emulationCanStart.invoke()) {
938 return 977 return
@@ -940,9 +979,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
940 979
941 when (state) { 980 when (state) {
942 State.STOPPED -> { 981 State.STOPPED -> {
943 val emulationThread = Thread({ 982 emulationThread = Thread({
944 Log.debug("[EmulationFragment] Starting emulation thread.") 983 Log.debug("[EmulationFragment] Starting emulation thread.")
945 NativeLibrary.run(gamePath) 984 NativeLibrary.run(gamePath, programIndex)
946 }, "NativeEmulation") 985 }, "NativeEmulation")
947 emulationThread.start() 986 emulationThread.start()
948 } 987 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
index b66f47fe7..d024493cd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -15,6 +15,12 @@ class EmulationViewModel : ViewModel() {
15 val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping 15 val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping
16 private val _isEmulationStopping = MutableStateFlow(false) 16 private val _isEmulationStopping = MutableStateFlow(false)
17 17
18 private val _emulationStopped = MutableStateFlow(false)
19 val emulationStopped = _emulationStopped.asStateFlow()
20
21 private val _programChanged = MutableStateFlow(-1)
22 val programChanged = _programChanged.asStateFlow()
23
18 val shaderProgress: StateFlow<Int> get() = _shaderProgress 24 val shaderProgress: StateFlow<Int> get() = _shaderProgress
19 private val _shaderProgress = MutableStateFlow(0) 25 private val _shaderProgress = MutableStateFlow(0)
20 26
@@ -35,6 +41,17 @@ class EmulationViewModel : ViewModel() {
35 _isEmulationStopping.value = value 41 _isEmulationStopping.value = value
36 } 42 }
37 43
44 fun setEmulationStopped(value: Boolean) {
45 if (value) {
46 _emulationStarted.value = false
47 }
48 _emulationStopped.value = value
49 }
50
51 fun setProgramChanged(programIndex: Int) {
52 _programChanged.value = programIndex
53 }
54
38 fun setShaderProgress(progress: Int) { 55 fun setShaderProgress(progress: Int) {
39 _shaderProgress.value = progress 56 _shaderProgress.value = progress
40 } 57 }
@@ -56,20 +73,4 @@ class EmulationViewModel : ViewModel() {
56 fun setDrawerOpen(value: Boolean) { 73 fun setDrawerOpen(value: Boolean) {
57 _drawerOpen.value = value 74 _drawerOpen.value = value
58 } 75 }
59
60 fun clear() {
61 setEmulationStarted(false)
62 setIsEmulationStopping(false)
63 setShaderProgress(0)
64 setTotalShaders(0)
65 setShaderMessage("")
66 }
67
68 companion object {
69 const val KEY_EMULATION_STARTED = "EmulationStarted"
70 const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting"
71 const val KEY_SHADER_PROGRESS = "ShaderProgress"
72 const val KEY_TOTAL_SHADERS = "TotalShaders"
73 const val KEY_SHADER_MESSAGE = "ShaderMessage"
74 }
75} 76}
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 96f2ad3d4..f30100bd8 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -19,6 +19,7 @@ static jmethodID s_exit_emulation_activity;
19static jmethodID s_disk_cache_load_progress; 19static jmethodID s_disk_cache_load_progress;
20static jmethodID s_on_emulation_started; 20static jmethodID s_on_emulation_started;
21static jmethodID s_on_emulation_stopped; 21static jmethodID s_on_emulation_stopped;
22static jmethodID s_on_program_changed;
22 23
23static jclass s_game_class; 24static jclass s_game_class;
24static jmethodID s_game_constructor; 25static jmethodID s_game_constructor;
@@ -123,6 +124,10 @@ jmethodID GetOnEmulationStopped() {
123 return s_on_emulation_stopped; 124 return s_on_emulation_stopped;
124} 125}
125 126
127jmethodID GetOnProgramChanged() {
128 return s_on_program_changed;
129}
130
126jclass GetGameClass() { 131jclass GetGameClass() {
127 return s_game_class; 132 return s_game_class;
128} 133}
@@ -306,6 +311,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
306 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); 311 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
307 s_on_emulation_stopped = 312 s_on_emulation_stopped =
308 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); 313 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
314 s_on_program_changed =
315 env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
309 316
310 const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); 317 const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
311 s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); 318 s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index a002e705d..00e48afc0 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -19,6 +19,7 @@ jmethodID GetExitEmulationActivity();
19jmethodID GetDiskCacheLoadProgress(); 19jmethodID GetDiskCacheLoadProgress();
20jmethodID GetOnEmulationStarted(); 20jmethodID GetOnEmulationStarted();
21jmethodID GetOnEmulationStopped(); 21jmethodID GetOnEmulationStopped();
22jmethodID GetOnProgramChanged();
22 23
23jclass GetGameClass(); 24jclass GetGameClass();
24jmethodID GetGameConstructor(); 25jmethodID GetGameConstructor();
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 3fd9a500c..958a77ac7 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -208,7 +208,8 @@ void EmulationSession::InitializeSystem(bool reload) {
208 m_system.GetFileSystemController().CreateFactories(*m_vfs); 208 m_system.GetFileSystemController().CreateFactories(*m_vfs);
209} 209}
210 210
211Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { 211Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
212 const std::size_t program_index) {
212 std::scoped_lock lock(m_mutex); 213 std::scoped_lock lock(m_mutex);
213 214
214 // Create the render window. 215 // Create the render window.
@@ -238,7 +239,8 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
238 ConfigureFilesystemProvider(filepath); 239 ConfigureFilesystemProvider(filepath);
239 240
240 // Load the ROM. 241 // Load the ROM.
241 m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); 242 m_load_result =
243 m_system.Load(EmulationSession::GetInstance().Window(), filepath, 0, program_index);
242 if (m_load_result != Core::SystemResultStatus::Success) { 244 if (m_load_result != Core::SystemResultStatus::Success) {
243 return m_load_result; 245 return m_load_result;
244 } 246 }
@@ -248,6 +250,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
248 m_system.GetCpuManager().OnGpuReady(); 250 m_system.GetCpuManager().OnGpuReady();
249 m_system.RegisterExitCallback([&] { HaltEmulation(); }); 251 m_system.RegisterExitCallback([&] { HaltEmulation(); });
250 252
253 // Register an ExecuteProgram callback such that Core can execute a sub-program
254 m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
255 m_next_program_index = program_index_;
256 EmulationSession::GetInstance().HaltEmulation();
257 });
258
251 OnEmulationStarted(); 259 OnEmulationStarted();
252 return Core::SystemResultStatus::Success; 260 return Core::SystemResultStatus::Success;
253} 261}
@@ -255,6 +263,11 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
255void EmulationSession::ShutdownEmulation() { 263void EmulationSession::ShutdownEmulation() {
256 std::scoped_lock lock(m_mutex); 264 std::scoped_lock lock(m_mutex);
257 265
266 if (m_next_program_index != -1) {
267 ChangeProgram(m_next_program_index);
268 m_next_program_index = -1;
269 }
270
258 m_is_running = false; 271 m_is_running = false;
259 272
260 // Unload user input. 273 // Unload user input.
@@ -402,6 +415,12 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
402 static_cast<jint>(result)); 415 static_cast<jint>(result));
403} 416}
404 417
418void EmulationSession::ChangeProgram(std::size_t program_index) {
419 JNIEnv* env = IDCache::GetEnvForThread();
420 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(),
421 static_cast<jint>(program_index));
422}
423
405u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { 424u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
406 auto program_id_string = GetJString(env, jprogramId); 425 auto program_id_string = GetJString(env, jprogramId);
407 try { 426 try {
@@ -411,7 +430,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
411 } 430 }
412} 431}
413 432
414static Core::SystemResultStatus RunEmulation(const std::string& filepath) { 433static Core::SystemResultStatus RunEmulation(const std::string& filepath,
434 const size_t program_index = 0) {
415 MicroProfileOnThreadCreate("EmuThread"); 435 MicroProfileOnThreadCreate("EmuThread");
416 SCOPE_EXIT({ MicroProfileShutdown(); }); 436 SCOPE_EXIT({ MicroProfileShutdown(); });
417 437
@@ -424,7 +444,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
424 444
425 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); 445 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
426 446
427 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); 447 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index);
428 if (result != Core::SystemResultStatus::Success) { 448 if (result != Core::SystemResultStatus::Success) {
429 return result; 449 return result;
430 } 450 }
@@ -689,11 +709,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
689 Settings::LogSettings(); 709 Settings::LogSettings();
690} 710}
691 711
692void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, 712void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
693 jstring j_path) { 713 jint j_program_index) {
694 const std::string path = GetJString(env, j_path); 714 const std::string path = GetJString(env, j_path);
695 715
696 const Core::SystemResultStatus result{RunEmulation(path)}; 716 const Core::SystemResultStatus result{RunEmulation(path, j_program_index)};
697 if (result != Core::SystemResultStatus::Success) { 717 if (result != Core::SystemResultStatus::Success) {
698 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), 718 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
699 IDCache::GetExitEmulationActivity(), static_cast<int>(result)); 719 IDCache::GetExitEmulationActivity(), static_cast<int>(result));
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index dadb138ad..bfe3fccca 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -45,7 +45,8 @@ public:
45 const Core::PerfStatsResults& PerfStats(); 45 const Core::PerfStatsResults& PerfStats();
46 void ConfigureFilesystemProvider(const std::string& filepath); 46 void ConfigureFilesystemProvider(const std::string& filepath);
47 void InitializeSystem(bool reload); 47 void InitializeSystem(bool reload);
48 Core::SystemResultStatus InitializeEmulation(const std::string& filepath); 48 Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
49 const std::size_t program_index = 0);
49 50
50 bool IsHandheldOnly(); 51 bool IsHandheldOnly();
51 void SetDeviceType([[maybe_unused]] int index, int type); 52 void SetDeviceType([[maybe_unused]] int index, int type);
@@ -60,6 +61,7 @@ public:
60private: 61private:
61 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); 62 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
62 static void OnEmulationStopped(Core::SystemResultStatus result); 63 static void OnEmulationStopped(Core::SystemResultStatus result);
64 static void ChangeProgram(std::size_t program_index);
63 65
64private: 66private:
65 // Window management 67 // Window management
@@ -84,4 +86,7 @@ private:
84 // Synchronization 86 // Synchronization
85 std::condition_variable_any m_cv; 87 std::condition_variable_any m_cv;
86 mutable std::mutex m_mutex; 88 mutable std::mutex m_mutex;
89
90 // Program index for next boot
91 std::atomic<s32> m_next_program_index = -1;
87}; 92};