diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/android/app/src/main/jni/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/android/app/src/main/jni/native.cpp | 713 | ||||
| -rw-r--r-- | src/android/app/src/main/jni/native.h | 84 |
3 files changed, 392 insertions, 406 deletions
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index e15d1480b..7193903da 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt | |||
| @@ -14,6 +14,7 @@ add_library(yuzu-android SHARED | |||
| 14 | id_cache.cpp | 14 | id_cache.cpp |
| 15 | id_cache.h | 15 | id_cache.h |
| 16 | native.cpp | 16 | native.cpp |
| 17 | native.h | ||
| 17 | native_config.cpp | 18 | native_config.cpp |
| 18 | uisettings.cpp | 19 | uisettings.cpp |
| 19 | ) | 20 | ) |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 598f4e8bf..629be3d81 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -33,7 +33,6 @@ | |||
| 33 | #include "core/crypto/key_manager.h" | 33 | #include "core/crypto/key_manager.h" |
| 34 | #include "core/file_sys/card_image.h" | 34 | #include "core/file_sys/card_image.h" |
| 35 | #include "core/file_sys/content_archive.h" | 35 | #include "core/file_sys/content_archive.h" |
| 36 | #include "core/file_sys/registered_cache.h" | ||
| 37 | #include "core/file_sys/submission_package.h" | 36 | #include "core/file_sys/submission_package.h" |
| 38 | #include "core/file_sys/vfs.h" | 37 | #include "core/file_sys/vfs.h" |
| 39 | #include "core/file_sys/vfs_real.h" | 38 | #include "core/file_sys/vfs_real.h" |
| @@ -48,514 +47,416 @@ | |||
| 48 | #include "core/hid/emulated_controller.h" | 47 | #include "core/hid/emulated_controller.h" |
| 49 | #include "core/hid/hid_core.h" | 48 | #include "core/hid/hid_core.h" |
| 50 | #include "core/hid/hid_types.h" | 49 | #include "core/hid/hid_types.h" |
| 51 | #include "core/hle/service/acc/profile_manager.h" | ||
| 52 | #include "core/hle/service/am/applet_ae.h" | 50 | #include "core/hle/service/am/applet_ae.h" |
| 53 | #include "core/hle/service/am/applet_oe.h" | 51 | #include "core/hle/service/am/applet_oe.h" |
| 54 | #include "core/hle/service/am/applets/applets.h" | 52 | #include "core/hle/service/am/applets/applets.h" |
| 55 | #include "core/hle/service/filesystem/filesystem.h" | 53 | #include "core/hle/service/filesystem/filesystem.h" |
| 56 | #include "core/loader/loader.h" | 54 | #include "core/loader/loader.h" |
| 57 | #include "core/perf_stats.h" | ||
| 58 | #include "jni/android_common/android_common.h" | 55 | #include "jni/android_common/android_common.h" |
| 59 | #include "jni/applets/software_keyboard.h" | ||
| 60 | #include "jni/config.h" | 56 | #include "jni/config.h" |
| 61 | #include "jni/emu_window/emu_window.h" | ||
| 62 | #include "jni/id_cache.h" | 57 | #include "jni/id_cache.h" |
| 63 | #include "video_core/rasterizer_interface.h" | 58 | #include "jni/native.h" |
| 64 | #include "video_core/renderer_base.h" | 59 | #include "video_core/renderer_base.h" |
| 65 | 60 | ||
| 66 | #define jconst [[maybe_unused]] const auto | 61 | #define jconst [[maybe_unused]] const auto |
| 67 | #define jauto [[maybe_unused]] auto | 62 | #define jauto [[maybe_unused]] auto |
| 68 | 63 | ||
| 69 | namespace { | 64 | static EmulationSession s_instance; |
| 70 | 65 | ||
| 71 | class EmulationSession final { | 66 | EmulationSession::EmulationSession() { |
| 72 | public: | 67 | m_vfs = std::make_shared<FileSys::RealVfsFilesystem>(); |
| 73 | EmulationSession() { | 68 | } |
| 74 | m_vfs = std::make_shared<FileSys::RealVfsFilesystem>(); | ||
| 75 | } | ||
| 76 | |||
| 77 | ~EmulationSession() = default; | ||
| 78 | |||
| 79 | static EmulationSession& GetInstance() { | ||
| 80 | return s_instance; | ||
| 81 | } | ||
| 82 | |||
| 83 | const Core::System& System() const { | ||
| 84 | return m_system; | ||
| 85 | } | ||
| 86 | 69 | ||
| 87 | Core::System& System() { | 70 | EmulationSession& EmulationSession::GetInstance() { |
| 88 | return m_system; | 71 | return s_instance; |
| 89 | } | 72 | } |
| 90 | 73 | ||
| 91 | const EmuWindow_Android& Window() const { | 74 | const Core::System& EmulationSession::System() const { |
| 92 | return *m_window; | 75 | return m_system; |
| 93 | } | 76 | } |
| 94 | 77 | ||
| 95 | EmuWindow_Android& Window() { | 78 | Core::System& EmulationSession::System() { |
| 96 | return *m_window; | 79 | return m_system; |
| 97 | } | 80 | } |
| 98 | 81 | ||
| 99 | ANativeWindow* NativeWindow() const { | 82 | const EmuWindow_Android& EmulationSession::Window() const { |
| 100 | return m_native_window; | 83 | return *m_window; |
| 101 | } | 84 | } |
| 102 | 85 | ||
| 103 | void SetNativeWindow(ANativeWindow* native_window) { | 86 | EmuWindow_Android& EmulationSession::Window() { |
| 104 | m_native_window = native_window; | 87 | return *m_window; |
| 105 | } | 88 | } |
| 106 | 89 | ||
| 107 | int InstallFileToNand(std::string filename, std::string file_extension) { | 90 | ANativeWindow* EmulationSession::NativeWindow() const { |
| 108 | jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, | 91 | return m_native_window; |
| 109 | std::size_t block_size) { | 92 | } |
| 110 | if (src == nullptr || dest == nullptr) { | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | if (!dest->Resize(src->GetSize())) { | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | 93 | ||
| 117 | using namespace Common::Literals; | 94 | void EmulationSession::SetNativeWindow(ANativeWindow* native_window) { |
| 118 | [[maybe_unused]] std::vector<u8> buffer(1_MiB); | 95 | m_native_window = native_window; |
| 96 | } | ||
| 119 | 97 | ||
| 120 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 98 | int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) { |
| 121 | jconst read = src->Read(buffer.data(), buffer.size(), i); | 99 | jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, |
| 122 | dest->Write(buffer.data(), read, i); | 100 | std::size_t block_size) { |
| 123 | } | 101 | if (src == nullptr || dest == nullptr) { |
| 124 | return true; | 102 | return false; |
| 125 | }; | ||
| 126 | |||
| 127 | enum InstallResult { | ||
| 128 | Success = 0, | ||
| 129 | SuccessFileOverwritten = 1, | ||
| 130 | InstallError = 2, | ||
| 131 | ErrorBaseGame = 3, | ||
| 132 | ErrorFilenameExtension = 4, | ||
| 133 | }; | ||
| 134 | |||
| 135 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 136 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | ||
| 137 | |||
| 138 | [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; | ||
| 139 | if (file_extension == "nsp") { | ||
| 140 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); | ||
| 141 | if (nsp->IsExtractedType()) { | ||
| 142 | return InstallError; | ||
| 143 | } | ||
| 144 | } else { | ||
| 145 | return ErrorFilenameExtension; | ||
| 146 | } | 103 | } |
| 147 | 104 | if (!dest->Resize(src->GetSize())) { | |
| 148 | if (!nsp) { | 105 | return false; |
| 149 | return InstallError; | ||
| 150 | } | 106 | } |
| 151 | 107 | ||
| 152 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { | 108 | using namespace Common::Literals; |
| 153 | return InstallError; | 109 | [[maybe_unused]] std::vector<u8> buffer(1_MiB); |
| 154 | } | ||
| 155 | 110 | ||
| 156 | jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( | 111 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 157 | *nsp, true, copy_func); | 112 | jconst read = src->Read(buffer.data(), buffer.size(), i); |
| 158 | 113 | dest->Write(buffer.data(), read, i); | |
| 159 | switch (res) { | ||
| 160 | case FileSys::InstallResult::Success: | ||
| 161 | return Success; | ||
| 162 | case FileSys::InstallResult::OverwriteExisting: | ||
| 163 | return SuccessFileOverwritten; | ||
| 164 | case FileSys::InstallResult::ErrorBaseInstall: | ||
| 165 | return ErrorBaseGame; | ||
| 166 | default: | ||
| 167 | return InstallError; | ||
| 168 | } | 114 | } |
| 169 | } | 115 | return true; |
| 116 | }; | ||
| 170 | 117 | ||
| 171 | void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, | 118 | enum InstallResult { |
| 172 | const std::string& custom_driver_name, | 119 | Success = 0, |
| 173 | const std::string& file_redirect_dir) { | 120 | SuccessFileOverwritten = 1, |
| 174 | #ifdef ARCHITECTURE_arm64 | 121 | InstallError = 2, |
| 175 | void* handle{}; | 122 | ErrorBaseGame = 3, |
| 176 | const char* file_redirect_dir_{}; | 123 | ErrorFilenameExtension = 4, |
| 177 | int featureFlags{}; | 124 | }; |
| 178 | |||
| 179 | // Enable driver file redirection when renderer debugging is enabled. | ||
| 180 | if (Settings::values.renderer_debug && file_redirect_dir.size()) { | ||
| 181 | featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; | ||
| 182 | file_redirect_dir_ = file_redirect_dir.c_str(); | ||
| 183 | } | ||
| 184 | 125 | ||
| 185 | // Try to load a custom driver. | 126 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 186 | if (custom_driver_name.size()) { | 127 | m_system.GetFileSystemController().CreateFactories(*m_vfs); |
| 187 | handle = adrenotools_open_libvulkan( | ||
| 188 | RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), | ||
| 189 | custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); | ||
| 190 | } | ||
| 191 | 128 | ||
| 192 | // Try to load the system driver. | 129 | [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; |
| 193 | if (!handle) { | 130 | if (file_extension == "nsp") { |
| 194 | handle = | 131 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); |
| 195 | adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), | 132 | if (nsp->IsExtractedType()) { |
| 196 | nullptr, nullptr, file_redirect_dir_, nullptr); | 133 | return InstallError; |
| 197 | } | 134 | } |
| 198 | 135 | } else { | |
| 199 | m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle); | 136 | return ErrorFilenameExtension; |
| 200 | #endif | ||
| 201 | } | 137 | } |
| 202 | 138 | ||
| 203 | bool IsRunning() const { | 139 | if (!nsp) { |
| 204 | return m_is_running; | 140 | return InstallError; |
| 205 | } | 141 | } |
| 206 | 142 | ||
| 207 | bool IsPaused() const { | 143 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { |
| 208 | return m_is_running && m_is_paused; | 144 | return InstallError; |
| 209 | } | 145 | } |
| 210 | 146 | ||
| 211 | const Core::PerfStatsResults& PerfStats() const { | 147 | jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, |
| 212 | std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); | 148 | copy_func); |
| 213 | return m_perf_stats; | ||
| 214 | } | ||
| 215 | 149 | ||
| 216 | void SurfaceChanged() { | 150 | switch (res) { |
| 217 | if (!IsRunning()) { | 151 | case FileSys::InstallResult::Success: |
| 218 | return; | 152 | return Success; |
| 219 | } | 153 | case FileSys::InstallResult::OverwriteExisting: |
| 220 | m_window->OnSurfaceChanged(m_native_window); | 154 | return SuccessFileOverwritten; |
| 155 | case FileSys::InstallResult::ErrorBaseInstall: | ||
| 156 | return ErrorBaseGame; | ||
| 157 | default: | ||
| 158 | return InstallError; | ||
| 221 | } | 159 | } |
| 160 | } | ||
| 222 | 161 | ||
| 223 | void ConfigureFilesystemProvider(const std::string& filepath) { | 162 | void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir, |
| 224 | const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); | 163 | const std::string& custom_driver_dir, |
| 225 | if (!file) { | 164 | const std::string& custom_driver_name, |
| 226 | return; | 165 | const std::string& file_redirect_dir) { |
| 227 | } | 166 | #ifdef ARCHITECTURE_arm64 |
| 228 | 167 | void* handle{}; | |
| 229 | auto loader = Loader::GetLoader(m_system, file); | 168 | const char* file_redirect_dir_{}; |
| 230 | if (!loader) { | 169 | int featureFlags{}; |
| 231 | return; | ||
| 232 | } | ||
| 233 | |||
| 234 | const auto file_type = loader->GetFileType(); | ||
| 235 | if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { | ||
| 236 | return; | ||
| 237 | } | ||
| 238 | 170 | ||
| 239 | u64 program_id = 0; | 171 | // Enable driver file redirection when renderer debugging is enabled. |
| 240 | const auto res2 = loader->ReadProgramId(program_id); | 172 | if (Settings::values.renderer_debug && file_redirect_dir.size()) { |
| 241 | if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { | 173 | featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; |
| 242 | m_manual_provider->AddEntry(FileSys::TitleType::Application, | 174 | file_redirect_dir_ = file_redirect_dir.c_str(); |
| 243 | FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), | ||
| 244 | program_id, file); | ||
| 245 | } else if (res2 == Loader::ResultStatus::Success && | ||
| 246 | (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { | ||
| 247 | const auto nsp = file_type == Loader::FileType::NSP | ||
| 248 | ? std::make_shared<FileSys::NSP>(file) | ||
| 249 | : FileSys::XCI{file}.GetSecurePartitionNSP(); | ||
| 250 | for (const auto& title : nsp->GetNCAs()) { | ||
| 251 | for (const auto& entry : title.second) { | ||
| 252 | m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, | ||
| 253 | entry.second->GetBaseFile()); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | 175 | } |
| 258 | 176 | ||
| 259 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { | 177 | // Try to load a custom driver. |
| 260 | std::scoped_lock lock(m_mutex); | 178 | if (custom_driver_name.size()) { |
| 261 | 179 | handle = adrenotools_open_libvulkan( | |
| 262 | // Create the render window. | 180 | RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), |
| 263 | m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, | 181 | custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); |
| 264 | m_vulkan_library); | 182 | } |
| 265 | |||
| 266 | m_system.SetFilesystem(m_vfs); | ||
| 267 | m_system.GetUserChannel().clear(); | ||
| 268 | |||
| 269 | // Initialize system. | ||
| 270 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); | ||
| 271 | m_software_keyboard = android_keyboard.get(); | ||
| 272 | m_system.SetShuttingDown(false); | ||
| 273 | m_system.ApplySettings(); | ||
| 274 | Settings::LogSettings(); | ||
| 275 | m_system.HIDCore().ReloadInputDevices(); | ||
| 276 | m_system.SetAppletFrontendSet({ | ||
| 277 | nullptr, // Amiibo Settings | ||
| 278 | nullptr, // Controller Selector | ||
| 279 | nullptr, // Error Display | ||
| 280 | nullptr, // Mii Editor | ||
| 281 | nullptr, // Parental Controls | ||
| 282 | nullptr, // Photo Viewer | ||
| 283 | nullptr, // Profile Selector | ||
| 284 | std::move(android_keyboard), // Software Keyboard | ||
| 285 | nullptr, // Web Browser | ||
| 286 | }); | ||
| 287 | |||
| 288 | // Initialize filesystem. | ||
| 289 | m_manual_provider = std::make_unique<FileSys::ManualContentProvider>(); | ||
| 290 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 291 | m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, | ||
| 292 | m_manual_provider.get()); | ||
| 293 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | ||
| 294 | ConfigureFilesystemProvider(filepath); | ||
| 295 | |||
| 296 | // Initialize account manager | ||
| 297 | m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); | ||
| 298 | |||
| 299 | // Load the ROM. | ||
| 300 | m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); | ||
| 301 | if (m_load_result != Core::SystemResultStatus::Success) { | ||
| 302 | return m_load_result; | ||
| 303 | } | ||
| 304 | |||
| 305 | // Complete initialization. | ||
| 306 | m_system.GPU().Start(); | ||
| 307 | m_system.GetCpuManager().OnGpuReady(); | ||
| 308 | m_system.RegisterExitCallback([&] { HaltEmulation(); }); | ||
| 309 | 183 | ||
| 310 | return Core::SystemResultStatus::Success; | 184 | // Try to load the system driver. |
| 185 | if (!handle) { | ||
| 186 | handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), | ||
| 187 | nullptr, nullptr, file_redirect_dir_, nullptr); | ||
| 311 | } | 188 | } |
| 312 | 189 | ||
| 313 | void ShutdownEmulation() { | 190 | m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle); |
| 314 | std::scoped_lock lock(m_mutex); | 191 | #endif |
| 192 | } | ||
| 315 | 193 | ||
| 316 | m_is_running = false; | 194 | bool EmulationSession::IsRunning() const { |
| 195 | return m_is_running; | ||
| 196 | } | ||
| 317 | 197 | ||
| 318 | // Unload user input. | 198 | bool EmulationSession::IsPaused() const { |
| 319 | m_system.HIDCore().UnloadInputDevices(); | 199 | return m_is_running && m_is_paused; |
| 200 | } | ||
| 320 | 201 | ||
| 321 | // Shutdown the main emulated process | 202 | const Core::PerfStatsResults& EmulationSession::PerfStats() const { |
| 322 | if (m_load_result == Core::SystemResultStatus::Success) { | 203 | std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); |
| 323 | m_system.DetachDebugger(); | 204 | return m_perf_stats; |
| 324 | m_system.ShutdownMainProcess(); | 205 | } |
| 325 | m_detached_tasks.WaitForAllTasks(); | ||
| 326 | m_load_result = Core::SystemResultStatus::ErrorNotInitialized; | ||
| 327 | m_window.reset(); | ||
| 328 | OnEmulationStopped(Core::SystemResultStatus::Success); | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | 206 | ||
| 332 | // Tear down the render window. | 207 | void EmulationSession::SurfaceChanged() { |
| 333 | m_window.reset(); | 208 | if (!IsRunning()) { |
| 209 | return; | ||
| 334 | } | 210 | } |
| 211 | m_window->OnSurfaceChanged(m_native_window); | ||
| 212 | } | ||
| 335 | 213 | ||
| 336 | void PauseEmulation() { | 214 | void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) { |
| 337 | std::scoped_lock lock(m_mutex); | 215 | const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); |
| 338 | m_system.Pause(); | 216 | if (!file) { |
| 339 | m_is_paused = true; | 217 | return; |
| 340 | } | 218 | } |
| 341 | 219 | ||
| 342 | void UnPauseEmulation() { | 220 | auto loader = Loader::GetLoader(m_system, file); |
| 343 | std::scoped_lock lock(m_mutex); | 221 | if (!loader) { |
| 344 | m_system.Run(); | 222 | return; |
| 345 | m_is_paused = false; | ||
| 346 | } | 223 | } |
| 347 | 224 | ||
| 348 | void HaltEmulation() { | 225 | const auto file_type = loader->GetFileType(); |
| 349 | std::scoped_lock lock(m_mutex); | 226 | if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { |
| 350 | m_is_running = false; | 227 | return; |
| 351 | m_cv.notify_one(); | ||
| 352 | } | 228 | } |
| 353 | 229 | ||
| 354 | void RunEmulation() { | 230 | u64 program_id = 0; |
| 355 | { | 231 | const auto res2 = loader->ReadProgramId(program_id); |
| 356 | std::scoped_lock lock(m_mutex); | 232 | if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { |
| 357 | m_is_running = true; | 233 | m_manual_provider->AddEntry(FileSys::TitleType::Application, |
| 234 | FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), | ||
| 235 | program_id, file); | ||
| 236 | } else if (res2 == Loader::ResultStatus::Success && | ||
| 237 | (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { | ||
| 238 | const auto nsp = file_type == Loader::FileType::NSP | ||
| 239 | ? std::make_shared<FileSys::NSP>(file) | ||
| 240 | : FileSys::XCI{file}.GetSecurePartitionNSP(); | ||
| 241 | for (const auto& title : nsp->GetNCAs()) { | ||
| 242 | for (const auto& entry : title.second) { | ||
| 243 | m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, | ||
| 244 | entry.second->GetBaseFile()); | ||
| 245 | } | ||
| 358 | } | 246 | } |
| 247 | } | ||
| 248 | } | ||
| 359 | 249 | ||
| 360 | // Load the disk shader cache. | 250 | Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { |
| 361 | if (Settings::values.use_disk_shader_cache.GetValue()) { | 251 | std::scoped_lock lock(m_mutex); |
| 362 | LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 252 | |
| 363 | m_system.Renderer().ReadRasterizer()->LoadDiskResources( | 253 | // Create the render window. |
| 364 | m_system.GetApplicationProcessProgramID(), std::stop_token{}, | 254 | m_window = |
| 365 | LoadDiskCacheProgress); | 255 | std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); |
| 366 | LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 256 | |
| 367 | } | 257 | m_system.SetFilesystem(m_vfs); |
| 258 | m_system.GetUserChannel().clear(); | ||
| 259 | |||
| 260 | // Initialize system. | ||
| 261 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); | ||
| 262 | m_software_keyboard = android_keyboard.get(); | ||
| 263 | m_system.SetShuttingDown(false); | ||
| 264 | m_system.ApplySettings(); | ||
| 265 | Settings::LogSettings(); | ||
| 266 | m_system.HIDCore().ReloadInputDevices(); | ||
| 267 | m_system.SetAppletFrontendSet({ | ||
| 268 | nullptr, // Amiibo Settings | ||
| 269 | nullptr, // Controller Selector | ||
| 270 | nullptr, // Error Display | ||
| 271 | nullptr, // Mii Editor | ||
| 272 | nullptr, // Parental Controls | ||
| 273 | nullptr, // Photo Viewer | ||
| 274 | nullptr, // Profile Selector | ||
| 275 | std::move(android_keyboard), // Software Keyboard | ||
| 276 | nullptr, // Web Browser | ||
| 277 | }); | ||
| 278 | |||
| 279 | // Initialize filesystem. | ||
| 280 | m_manual_provider = std::make_unique<FileSys::ManualContentProvider>(); | ||
| 281 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 282 | m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, | ||
| 283 | m_manual_provider.get()); | ||
| 284 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | ||
| 285 | ConfigureFilesystemProvider(filepath); | ||
| 286 | |||
| 287 | // Initialize account manager | ||
| 288 | m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); | ||
| 289 | |||
| 290 | // Load the ROM. | ||
| 291 | m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); | ||
| 292 | if (m_load_result != Core::SystemResultStatus::Success) { | ||
| 293 | return m_load_result; | ||
| 294 | } | ||
| 295 | |||
| 296 | // Complete initialization. | ||
| 297 | m_system.GPU().Start(); | ||
| 298 | m_system.GetCpuManager().OnGpuReady(); | ||
| 299 | m_system.RegisterExitCallback([&] { HaltEmulation(); }); | ||
| 368 | 300 | ||
| 369 | void(m_system.Run()); | 301 | return Core::SystemResultStatus::Success; |
| 302 | } | ||
| 370 | 303 | ||
| 371 | if (m_system.DebuggerEnabled()) { | 304 | void EmulationSession::ShutdownEmulation() { |
| 372 | m_system.InitializeDebugger(); | 305 | std::scoped_lock lock(m_mutex); |
| 373 | } | ||
| 374 | 306 | ||
| 375 | OnEmulationStarted(); | 307 | m_is_running = false; |
| 376 | 308 | ||
| 377 | while (true) { | 309 | // Unload user input. |
| 378 | { | 310 | m_system.HIDCore().UnloadInputDevices(); |
| 379 | [[maybe_unused]] std::unique_lock lock(m_mutex); | ||
| 380 | if (m_cv.wait_for(lock, std::chrono::milliseconds(800), | ||
| 381 | [&]() { return !m_is_running; })) { | ||
| 382 | // Emulation halted. | ||
| 383 | break; | ||
| 384 | } | ||
| 385 | } | ||
| 386 | { | ||
| 387 | // Refresh performance stats. | ||
| 388 | std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); | ||
| 389 | m_perf_stats = m_system.GetAndResetPerfStats(); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | std::string GetRomTitle(const std::string& path) { | ||
| 395 | return GetRomMetadata(path).title; | ||
| 396 | } | ||
| 397 | |||
| 398 | std::vector<u8> GetRomIcon(const std::string& path) { | ||
| 399 | return GetRomMetadata(path).icon; | ||
| 400 | } | ||
| 401 | 311 | ||
| 402 | bool GetIsHomebrew(const std::string& path) { | 312 | // Shutdown the main emulated process |
| 403 | return GetRomMetadata(path).isHomebrew; | 313 | if (m_load_result == Core::SystemResultStatus::Success) { |
| 314 | m_system.DetachDebugger(); | ||
| 315 | m_system.ShutdownMainProcess(); | ||
| 316 | m_detached_tasks.WaitForAllTasks(); | ||
| 317 | m_load_result = Core::SystemResultStatus::ErrorNotInitialized; | ||
| 318 | m_window.reset(); | ||
| 319 | OnEmulationStopped(Core::SystemResultStatus::Success); | ||
| 320 | return; | ||
| 404 | } | 321 | } |
| 405 | 322 | ||
| 406 | void ResetRomMetadata() { | 323 | // Tear down the render window. |
| 407 | m_rom_metadata_cache.clear(); | 324 | m_window.reset(); |
| 408 | } | 325 | } |
| 409 | 326 | ||
| 410 | bool IsHandheldOnly() { | 327 | void EmulationSession::PauseEmulation() { |
| 411 | jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); | 328 | std::scoped_lock lock(m_mutex); |
| 329 | m_system.Pause(); | ||
| 330 | m_is_paused = true; | ||
| 331 | } | ||
| 412 | 332 | ||
| 413 | if (npad_style_set.fullkey == 1) { | 333 | void EmulationSession::UnPauseEmulation() { |
| 414 | return false; | 334 | std::scoped_lock lock(m_mutex); |
| 415 | } | 335 | m_system.Run(); |
| 336 | m_is_paused = false; | ||
| 337 | } | ||
| 416 | 338 | ||
| 417 | if (npad_style_set.handheld == 0) { | 339 | void EmulationSession::HaltEmulation() { |
| 418 | return false; | 340 | std::scoped_lock lock(m_mutex); |
| 419 | } | 341 | m_is_running = false; |
| 342 | m_cv.notify_one(); | ||
| 343 | } | ||
| 420 | 344 | ||
| 421 | return !Settings::IsDockedMode(); | 345 | void EmulationSession::RunEmulation() { |
| 346 | { | ||
| 347 | std::scoped_lock lock(m_mutex); | ||
| 348 | m_is_running = true; | ||
| 422 | } | 349 | } |
| 423 | 350 | ||
| 424 | void SetDeviceType([[maybe_unused]] int index, int type) { | 351 | // Load the disk shader cache. |
| 425 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 352 | if (Settings::values.use_disk_shader_cache.GetValue()) { |
| 426 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); | 353 | LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 354 | m_system.Renderer().ReadRasterizer()->LoadDiskResources( | ||
| 355 | m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress); | ||
| 356 | LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | ||
| 427 | } | 357 | } |
| 428 | 358 | ||
| 429 | void OnGamepadConnectEvent([[maybe_unused]] int index) { | 359 | void(m_system.Run()); |
| 430 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | ||
| 431 | |||
| 432 | // Ensure that player1 is configured correctly and handheld disconnected | ||
| 433 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { | ||
| 434 | jauto handheld = | ||
| 435 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 436 | 360 | ||
| 437 | if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { | 361 | if (m_system.DebuggerEnabled()) { |
| 438 | handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | 362 | m_system.InitializeDebugger(); |
| 439 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | 363 | } |
| 440 | handheld->Disconnect(); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | 364 | ||
| 444 | // Ensure that handheld is configured correctly and player 1 disconnected | 365 | OnEmulationStarted(); |
| 445 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { | ||
| 446 | jauto player1 = | ||
| 447 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 448 | 366 | ||
| 449 | if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { | 367 | while (true) { |
| 450 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | 368 | { |
| 451 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | 369 | [[maybe_unused]] std::unique_lock lock(m_mutex); |
| 452 | player1->Disconnect(); | 370 | if (m_cv.wait_for(lock, std::chrono::milliseconds(800), |
| 371 | [&]() { return !m_is_running; })) { | ||
| 372 | // Emulation halted. | ||
| 373 | break; | ||
| 453 | } | 374 | } |
| 454 | } | 375 | } |
| 455 | 376 | { | |
| 456 | if (!controller->IsConnected()) { | 377 | // Refresh performance stats. |
| 457 | controller->Connect(); | 378 | std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); |
| 379 | m_perf_stats = m_system.GetAndResetPerfStats(); | ||
| 458 | } | 380 | } |
| 459 | } | 381 | } |
| 382 | } | ||
| 383 | |||
| 384 | bool EmulationSession::IsHandheldOnly() { | ||
| 385 | jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); | ||
| 460 | 386 | ||
| 461 | void OnGamepadDisconnectEvent([[maybe_unused]] int index) { | 387 | if (npad_style_set.fullkey == 1) { |
| 462 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 388 | return false; |
| 463 | controller->Disconnect(); | ||
| 464 | } | 389 | } |
| 465 | 390 | ||
| 466 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { | 391 | if (npad_style_set.handheld == 0) { |
| 467 | return m_software_keyboard; | 392 | return false; |
| 468 | } | 393 | } |
| 469 | 394 | ||
| 470 | private: | 395 | return !Settings::IsDockedMode(); |
| 471 | struct RomMetadata { | 396 | } |
| 472 | std::string title; | ||
| 473 | std::vector<u8> icon; | ||
| 474 | bool isHomebrew; | ||
| 475 | }; | ||
| 476 | 397 | ||
| 477 | RomMetadata GetRomMetadata(const std::string& path) { | 398 | void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) { |
| 478 | if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { | 399 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 479 | return search->second; | 400 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); |
| 480 | } | 401 | } |
| 481 | 402 | ||
| 482 | return CacheRomMetadata(path); | 403 | void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) { |
| 483 | } | 404 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 484 | 405 | ||
| 485 | RomMetadata CacheRomMetadata(const std::string& path) { | 406 | // Ensure that player1 is configured correctly and handheld disconnected |
| 486 | jconst file = Core::GetGameFileFromPath(m_vfs, path); | 407 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { |
| 487 | jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); | 408 | jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 488 | 409 | ||
| 489 | RomMetadata entry; | 410 | if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { |
| 490 | loader->ReadTitle(entry.title); | 411 | handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); |
| 491 | loader->ReadIcon(entry.icon); | 412 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); |
| 492 | if (loader->GetFileType() == Loader::FileType::NRO) { | 413 | handheld->Disconnect(); |
| 493 | jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get()); | ||
| 494 | entry.isHomebrew = loader_nro->IsHomebrew(); | ||
| 495 | } else { | ||
| 496 | entry.isHomebrew = false; | ||
| 497 | } | 414 | } |
| 498 | |||
| 499 | m_rom_metadata_cache[path] = entry; | ||
| 500 | |||
| 501 | return entry; | ||
| 502 | } | 415 | } |
| 503 | 416 | ||
| 504 | private: | 417 | // Ensure that handheld is configured correctly and player 1 disconnected |
| 505 | static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { | 418 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { |
| 506 | JNIEnv* env = IDCache::GetEnvForThread(); | 419 | jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 507 | env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), | ||
| 508 | IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), | ||
| 509 | static_cast<jint>(progress), static_cast<jint>(max)); | ||
| 510 | } | ||
| 511 | 420 | ||
| 512 | static void OnEmulationStarted() { | 421 | if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { |
| 513 | JNIEnv* env = IDCache::GetEnvForThread(); | 422 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); |
| 514 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | 423 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); |
| 515 | IDCache::GetOnEmulationStarted()); | 424 | player1->Disconnect(); |
| 425 | } | ||
| 516 | } | 426 | } |
| 517 | 427 | ||
| 518 | static void OnEmulationStopped(Core::SystemResultStatus result) { | 428 | if (!controller->IsConnected()) { |
| 519 | JNIEnv* env = IDCache::GetEnvForThread(); | 429 | controller->Connect(); |
| 520 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | ||
| 521 | IDCache::GetOnEmulationStopped(), static_cast<jint>(result)); | ||
| 522 | } | 430 | } |
| 431 | } | ||
| 523 | 432 | ||
| 524 | private: | 433 | void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) { |
| 525 | static EmulationSession s_instance; | 434 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 526 | 435 | controller->Disconnect(); | |
| 527 | // Frontend management | 436 | } |
| 528 | std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache; | ||
| 529 | |||
| 530 | // Window management | ||
| 531 | std::unique_ptr<EmuWindow_Android> m_window; | ||
| 532 | ANativeWindow* m_native_window{}; | ||
| 533 | |||
| 534 | // Core emulation | ||
| 535 | Core::System m_system; | ||
| 536 | InputCommon::InputSubsystem m_input_subsystem; | ||
| 537 | Common::DetachedTasks m_detached_tasks; | ||
| 538 | Core::PerfStatsResults m_perf_stats{}; | ||
| 539 | std::shared_ptr<FileSys::VfsFilesystem> m_vfs; | ||
| 540 | Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; | ||
| 541 | std::atomic<bool> m_is_running = false; | ||
| 542 | std::atomic<bool> m_is_paused = false; | ||
| 543 | SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; | ||
| 544 | std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; | ||
| 545 | std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; | ||
| 546 | 437 | ||
| 547 | // GPU driver parameters | 438 | SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { |
| 548 | std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; | 439 | return m_software_keyboard; |
| 440 | } | ||
| 549 | 441 | ||
| 550 | // Synchronization | 442 | void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, |
| 551 | std::condition_variable_any m_cv; | 443 | int max) { |
| 552 | mutable std::mutex m_perf_stats_mutex; | 444 | JNIEnv* env = IDCache::GetEnvForThread(); |
| 553 | mutable std::mutex m_mutex; | 445 | env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), |
| 554 | }; | 446 | IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), |
| 447 | static_cast<jint>(progress), static_cast<jint>(max)); | ||
| 448 | } | ||
| 555 | 449 | ||
| 556 | /*static*/ EmulationSession EmulationSession::s_instance; | 450 | void EmulationSession::OnEmulationStarted() { |
| 451 | JNIEnv* env = IDCache::GetEnvForThread(); | ||
| 452 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); | ||
| 453 | } | ||
| 557 | 454 | ||
| 558 | } // Anonymous namespace | 455 | void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { |
| 456 | JNIEnv* env = IDCache::GetEnvForThread(); | ||
| 457 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), | ||
| 458 | static_cast<jint>(result)); | ||
| 459 | } | ||
| 559 | 460 | ||
| 560 | static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | 461 | static Core::SystemResultStatus RunEmulation(const std::string& filepath) { |
| 561 | Common::Log::Initialize(); | 462 | Common::Log::Initialize(); |
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h new file mode 100644 index 000000000..2eb5c4349 --- /dev/null +++ b/src/android/app/src/main/jni/native.h | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <android/native_window_jni.h> | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/perf_stats.h" | ||
| 7 | #include "jni/emu_window/emu_window.h" | ||
| 8 | #include "jni/applets/software_keyboard.h" | ||
| 9 | #include "video_core/rasterizer_interface.h" | ||
| 10 | #include "common/detached_tasks.h" | ||
| 11 | #include "core/hle/service/acc/profile_manager.h" | ||
| 12 | #include "core/file_sys/registered_cache.h" | ||
| 13 | |||
| 14 | #pragma once | ||
| 15 | |||
| 16 | class EmulationSession final { | ||
| 17 | public: | ||
| 18 | explicit EmulationSession(); | ||
| 19 | ~EmulationSession() = default; | ||
| 20 | |||
| 21 | static EmulationSession& GetInstance(); | ||
| 22 | const Core::System& System() const; | ||
| 23 | Core::System& System(); | ||
| 24 | |||
| 25 | const EmuWindow_Android& Window() const; | ||
| 26 | EmuWindow_Android& Window(); | ||
| 27 | ANativeWindow* NativeWindow() const; | ||
| 28 | void SetNativeWindow(ANativeWindow* native_window); | ||
| 29 | void SurfaceChanged(); | ||
| 30 | |||
| 31 | int InstallFileToNand(std::string filename, std::string file_extension); | ||
| 32 | void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, | ||
| 33 | const std::string& custom_driver_name, | ||
| 34 | const std::string& file_redirect_dir); | ||
| 35 | |||
| 36 | bool IsRunning() const; | ||
| 37 | bool IsPaused() const; | ||
| 38 | void PauseEmulation(); | ||
| 39 | void UnPauseEmulation(); | ||
| 40 | void HaltEmulation(); | ||
| 41 | void RunEmulation(); | ||
| 42 | void ShutdownEmulation(); | ||
| 43 | |||
| 44 | const Core::PerfStatsResults& PerfStats() const; | ||
| 45 | void ConfigureFilesystemProvider(const std::string& filepath); | ||
| 46 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath); | ||
| 47 | |||
| 48 | bool IsHandheldOnly(); | ||
| 49 | void SetDeviceType([[maybe_unused]] int index, int type); | ||
| 50 | void OnGamepadConnectEvent([[maybe_unused]] int index); | ||
| 51 | void OnGamepadDisconnectEvent([[maybe_unused]] int index); | ||
| 52 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); | ||
| 53 | |||
| 54 | private: | ||
| 55 | static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); | ||
| 56 | static void OnEmulationStarted(); | ||
| 57 | static void OnEmulationStopped(Core::SystemResultStatus result); | ||
| 58 | |||
| 59 | private: | ||
| 60 | // Window management | ||
| 61 | std::unique_ptr<EmuWindow_Android> m_window; | ||
| 62 | ANativeWindow* m_native_window{}; | ||
| 63 | |||
| 64 | // Core emulation | ||
| 65 | Core::System m_system; | ||
| 66 | InputCommon::InputSubsystem m_input_subsystem; | ||
| 67 | Common::DetachedTasks m_detached_tasks; | ||
| 68 | Core::PerfStatsResults m_perf_stats{}; | ||
| 69 | std::shared_ptr<FileSys::VfsFilesystem> m_vfs; | ||
| 70 | Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; | ||
| 71 | std::atomic<bool> m_is_running = false; | ||
| 72 | std::atomic<bool> m_is_paused = false; | ||
| 73 | SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; | ||
| 74 | std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; | ||
| 75 | std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; | ||
| 76 | |||
| 77 | // GPU driver parameters | ||
| 78 | std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; | ||
| 79 | |||
| 80 | // Synchronization | ||
| 81 | std::condition_variable_any m_cv; | ||
| 82 | mutable std::mutex m_perf_stats_mutex; | ||
| 83 | mutable std::mutex m_mutex; | ||
| 84 | }; | ||