| | |
| | |
| |
|
| | #include <array> |
| | #include <atomic> |
| | #include <exception> |
| | #include <memory> |
| | #include <utility> |
| |
|
| | #include "audio_core/audio_core.h" |
| | #include "common/fs/fs.h" |
| | #include "common/logging/log.h" |
| | #include "common/microprofile.h" |
| | #include "common/settings.h" |
| | #include "common/settings_enums.h" |
| | #include "common/string_util.h" |
| | #include "core/arm/exclusive_monitor.h" |
| | #include "core/core.h" |
| | #include "core/core_timing.h" |
| | #include "core/cpu_manager.h" |
| | #include "core/debugger/debugger.h" |
| | #include "core/device_memory.h" |
| | #include "core/file_sys/bis_factory.h" |
| | #include "core/file_sys/fs_filesystem.h" |
| | #include "core/file_sys/patch_manager.h" |
| | #include "core/file_sys/registered_cache.h" |
| | #include "core/file_sys/romfs_factory.h" |
| | #include "core/file_sys/savedata_factory.h" |
| | #include "core/file_sys/vfs/vfs_concat.h" |
| | #include "core/file_sys/vfs/vfs_real.h" |
| | #include "core/gpu_dirty_memory_manager.h" |
| | #include "core/hle/kernel/k_memory_manager.h" |
| | #include "core/hle/kernel/k_process.h" |
| | #include "core/hle/kernel/k_resource_limit.h" |
| | #include "core/hle/kernel/k_scheduler.h" |
| | #include "core/hle/kernel/kernel.h" |
| | #include "core/hle/kernel/physical_core.h" |
| | #include "core/hle/service/acc/profile_manager.h" |
| | #include "core/hle/service/am/applet_manager.h" |
| | #include "core/hle/service/am/frontend/applets.h" |
| | #include "core/hle/service/apm/apm_controller.h" |
| | #include "core/hle/service/filesystem/filesystem.h" |
| | #include "core/hle/service/glue/glue_manager.h" |
| | #include "core/hle/service/glue/time/static.h" |
| | #include "core/hle/service/psc/time/static.h" |
| | #include "core/hle/service/psc/time/steady_clock.h" |
| | #include "core/hle/service/psc/time/system_clock.h" |
| | #include "core/hle/service/psc/time/time_zone_service.h" |
| | #include "core/hle/service/service.h" |
| | #include "core/hle/service/services.h" |
| | #include "core/hle/service/set/system_settings_server.h" |
| | #include "core/hle/service/sm/sm.h" |
| | #include "core/internal_network/network.h" |
| | #include "core/loader/loader.h" |
| | #include "core/memory.h" |
| | #include "core/memory/cheat_engine.h" |
| | #include "core/perf_stats.h" |
| | #include "core/reporter.h" |
| | #include "core/telemetry_session.h" |
| | #include "core/tools/freezer.h" |
| | #include "core/tools/renderdoc.h" |
| | #include "hid_core/hid_core.h" |
| | #include "network/network.h" |
| | #include "video_core/host1x/host1x.h" |
| | #include "video_core/renderer_base.h" |
| | #include "video_core/video_core.h" |
| |
|
| | MICROPROFILE_DEFINE(ARM_CPU0, "ARM", "CPU 0", MP_RGB(255, 64, 64)); |
| | MICROPROFILE_DEFINE(ARM_CPU1, "ARM", "CPU 1", MP_RGB(255, 64, 64)); |
| | MICROPROFILE_DEFINE(ARM_CPU2, "ARM", "CPU 2", MP_RGB(255, 64, 64)); |
| | MICROPROFILE_DEFINE(ARM_CPU3, "ARM", "CPU 3", MP_RGB(255, 64, 64)); |
| |
|
| | namespace Core { |
| |
|
| | namespace { |
| |
|
| | FileSys::StorageId GetStorageIdForFrontendSlot( |
| | std::optional<FileSys::ContentProviderUnionSlot> slot) { |
| | if (!slot.has_value()) { |
| | return FileSys::StorageId::None; |
| | } |
| |
|
| | switch (*slot) { |
| | case FileSys::ContentProviderUnionSlot::UserNAND: |
| | return FileSys::StorageId::NandUser; |
| | case FileSys::ContentProviderUnionSlot::SysNAND: |
| | return FileSys::StorageId::NandSystem; |
| | case FileSys::ContentProviderUnionSlot::SDMC: |
| | return FileSys::StorageId::SdCard; |
| | case FileSys::ContentProviderUnionSlot::FrontendManual: |
| | return FileSys::StorageId::Host; |
| | default: |
| | return FileSys::StorageId::None; |
| | } |
| | } |
| |
|
| | } |
| |
|
| | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| | const std::string& path) { |
| | |
| | std::string dir_name; |
| | std::string filename; |
| | Common::SplitPath(path, &dir_name, &filename, nullptr); |
| |
|
| | if (filename == "00") { |
| | const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read); |
| | std::vector<FileSys::VirtualFile> concat; |
| |
|
| | for (u32 i = 0; i < 0x10; ++i) { |
| | const auto file_name = fmt::format("{:02X}", i); |
| | auto next = dir->GetFile(file_name); |
| |
|
| | if (next != nullptr) { |
| | concat.push_back(std::move(next)); |
| | } else { |
| | next = dir->GetFile(file_name); |
| |
|
| | if (next == nullptr) { |
| | break; |
| | } |
| |
|
| | concat.push_back(std::move(next)); |
| | } |
| | } |
| |
|
| | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(), |
| | std::move(concat)); |
| | } |
| |
|
| | if (Common::FS::IsDir(path)) { |
| | return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read); |
| | } |
| |
|
| | return vfs->OpenFile(path, FileSys::OpenMode::Read); |
| | } |
| |
|
| | struct System::Impl { |
| | explicit Impl(System& system) |
| | : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, |
| | reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {} |
| |
|
| | void Initialize(System& system) { |
| | device_memory = std::make_unique<Core::DeviceMemory>(); |
| |
|
| | is_multicore = Settings::values.use_multi_core.GetValue(); |
| | extended_memory_layout = |
| | Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; |
| |
|
| | core_timing.SetMulticore(is_multicore); |
| | core_timing.Initialize([&system]() { system.RegisterHostThread(); }); |
| |
|
| | |
| | if (virtual_filesystem == nullptr) { |
| | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| | } |
| | if (content_provider == nullptr) { |
| | content_provider = std::make_unique<FileSys::ContentProviderUnion>(); |
| | } |
| |
|
| | |
| | frontend_applets.SetDefaultAppletsIfMissing(); |
| |
|
| | is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
| |
|
| | kernel.SetMulticore(is_multicore); |
| | cpu_manager.SetMulticore(is_multicore); |
| | cpu_manager.SetAsyncGpu(is_async_gpu); |
| | } |
| |
|
| | void ReinitializeIfNecessary(System& system) { |
| | const bool must_reinitialize = |
| | is_multicore != Settings::values.use_multi_core.GetValue() || |
| | extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() != |
| | Settings::MemoryLayout::Memory_4Gb); |
| |
|
| | if (!must_reinitialize) { |
| | return; |
| | } |
| |
|
| | LOG_DEBUG(Kernel, "Re-initializing"); |
| |
|
| | is_multicore = Settings::values.use_multi_core.GetValue(); |
| | extended_memory_layout = |
| | Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; |
| |
|
| | Initialize(system); |
| | } |
| |
|
| | void RefreshTime(System& system) { |
| | if (!system.IsPoweredOn()) { |
| | return; |
| | } |
| |
|
| | auto settings_service = |
| | system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", |
| | true); |
| | auto static_service_a = |
| | system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true); |
| |
|
| | auto static_service_s = |
| | system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true); |
| |
|
| | std::shared_ptr<Service::PSC::Time::SystemClock> user_clock; |
| | static_service_a->GetStandardUserSystemClock(&user_clock); |
| |
|
| | std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; |
| | static_service_a->GetStandardLocalSystemClock(&local_clock); |
| |
|
| | std::shared_ptr<Service::PSC::Time::SystemClock> network_clock; |
| | static_service_s->GetStandardNetworkSystemClock(&network_clock); |
| |
|
| | std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service; |
| | static_service_a->GetTimeZoneService(&timezone_service); |
| |
|
| | Service::PSC::Time::LocationName name{}; |
| | auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); |
| | std::memcpy(name.data(), new_name.data(), std::min(name.size(), new_name.size())); |
| |
|
| | timezone_service->SetDeviceLocationName(name); |
| |
|
| | u64 time_offset = 0; |
| | if (Settings::values.custom_rtc_enabled) { |
| | time_offset = Settings::values.custom_rtc_offset.GetValue(); |
| | } |
| |
|
| | const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); |
| | const u64 current_time = |
| | +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); |
| | const u64 new_time = current_time + time_offset; |
| |
|
| | Service::PSC::Time::SystemClockContext context{}; |
| | settings_service->SetUserSystemClockContext(context); |
| | user_clock->SetCurrentTime(new_time); |
| |
|
| | local_clock->SetCurrentTime(new_time); |
| |
|
| | network_clock->GetSystemClockContext(&context); |
| | settings_service->SetNetworkSystemClockContext(context); |
| | network_clock->SetCurrentTime(new_time); |
| | } |
| |
|
| | void Run() { |
| | std::unique_lock<std::mutex> lk(suspend_guard); |
| |
|
| | kernel.SuspendEmulation(false); |
| | core_timing.SyncPause(false); |
| | is_paused.store(false, std::memory_order_relaxed); |
| | } |
| |
|
| | void Pause() { |
| | std::unique_lock<std::mutex> lk(suspend_guard); |
| |
|
| | core_timing.SyncPause(true); |
| | kernel.SuspendEmulation(true); |
| | is_paused.store(true, std::memory_order_relaxed); |
| | } |
| |
|
| | bool IsPaused() const { |
| | return is_paused.load(std::memory_order_relaxed); |
| | } |
| |
|
| | std::unique_lock<std::mutex> StallApplication() { |
| | std::unique_lock<std::mutex> lk(suspend_guard); |
| | kernel.SuspendEmulation(true); |
| | core_timing.SyncPause(true); |
| | return lk; |
| | } |
| |
|
| | void UnstallApplication() { |
| | if (!IsPaused()) { |
| | core_timing.SyncPause(false); |
| | kernel.SuspendEmulation(false); |
| | } |
| | } |
| |
|
| | void SetNVDECActive(bool is_nvdec_active) { |
| | nvdec_active = is_nvdec_active; |
| | } |
| |
|
| | bool GetNVDECActive() { |
| | return nvdec_active; |
| | } |
| |
|
| | void InitializeDebugger(System& system, u16 port) { |
| | debugger = std::make_unique<Debugger>(system, port); |
| | } |
| |
|
| | void InitializeKernel(System& system) { |
| | LOG_DEBUG(Core, "initialized OK"); |
| |
|
| | |
| | ReinitializeIfNecessary(system); |
| |
|
| | kernel.Initialize(); |
| | cpu_manager.Initialize(); |
| | } |
| |
|
| | SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { |
| | |
| | arp_manager.ResetAll(); |
| |
|
| | telemetry_session = std::make_unique<Core::TelemetrySession>(); |
| |
|
| | host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system); |
| | gpu_core = VideoCore::CreateGPU(emu_window, system); |
| | if (!gpu_core) { |
| | return SystemResultStatus::ErrorVideoCore; |
| | } |
| |
|
| | audio_core = std::make_unique<AudioCore::AudioCore>(system); |
| |
|
| | service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); |
| | services = |
| | std::make_unique<Service::Services>(service_manager, system, stop_event.get_token()); |
| |
|
| | is_powered_on = true; |
| | exit_locked = false; |
| | exit_requested = false; |
| |
|
| | microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); |
| | microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); |
| | microprofile_cpu[2] = MICROPROFILE_TOKEN(ARM_CPU2); |
| | microprofile_cpu[3] = MICROPROFILE_TOKEN(ARM_CPU3); |
| |
|
| | if (Settings::values.enable_renderdoc_hotkey) { |
| | renderdoc_api = std::make_unique<Tools::RenderdocAPI>(); |
| | } |
| |
|
| | LOG_DEBUG(Core, "Initialized OK"); |
| |
|
| | return SystemResultStatus::Success; |
| | } |
| |
|
| | SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| | const std::string& filepath, |
| | Service::AM::FrontendAppletParameters& params) { |
| | app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), |
| | params.program_id, params.program_index); |
| |
|
| | if (!app_loader) { |
| | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
| | return SystemResultStatus::ErrorGetLoader; |
| | } |
| |
|
| | if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) { |
| | LOG_ERROR(Core, "Failed to find title id for ROM!"); |
| | } |
| |
|
| | std::string name = "Unknown program"; |
| | if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { |
| | LOG_ERROR(Core, "Failed to read title for ROM!"); |
| | } |
| |
|
| | LOG_INFO(Core, "Loading {} ({})", name, params.program_id); |
| |
|
| | InitializeKernel(system); |
| |
|
| | |
| | auto main_process = Kernel::KProcess::Create(system.Kernel()); |
| | Kernel::KProcess::Register(system.Kernel(), main_process); |
| | kernel.AppendNewProcess(main_process); |
| | kernel.MakeApplicationProcess(main_process); |
| | const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); |
| | if (load_result != Loader::ResultStatus::Success) { |
| | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result); |
| | ShutdownMainProcess(); |
| |
|
| | return static_cast<SystemResultStatus>( |
| | static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result)); |
| | } |
| |
|
| | |
| | SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; |
| | if (init_result != SystemResultStatus::Success) { |
| | LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", |
| | static_cast<int>(init_result)); |
| | ShutdownMainProcess(); |
| | return init_result; |
| | } |
| |
|
| | AddGlueRegistrationForProcess(*app_loader, *main_process); |
| | telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); |
| |
|
| | |
| | if (cheat_engine) { |
| | cheat_engine->Initialize(); |
| | } |
| |
|
| | |
| | applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(), |
| | params); |
| |
|
| | |
| | main_process->Run(load_parameters->main_thread_priority, |
| | load_parameters->main_thread_stack_size); |
| | main_process->Close(); |
| |
|
| | if (Settings::values.gamecard_inserted) { |
| | if (Settings::values.gamecard_current_game) { |
| | fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); |
| | } else if (!Settings::values.gamecard_path.GetValue().empty()) { |
| | const auto& gamecard_path = Settings::values.gamecard_path.GetValue(); |
| | fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path)); |
| | } |
| | } |
| |
|
| | perf_stats = std::make_unique<PerfStats>(params.program_id); |
| | |
| | GetAndResetPerfStats(); |
| | perf_stats->BeginSystemFrame(); |
| |
|
| | std::string title_version; |
| | const FileSys::PatchManager pm(params.program_id, system.GetFileSystemController(), |
| | system.GetContentProvider()); |
| | const auto metadata = pm.GetControlMetadata(); |
| | if (metadata.first != nullptr) { |
| | title_version = metadata.first->GetVersionString(); |
| | } |
| | if (auto room_member = room_network.GetRoomMember().lock()) { |
| | Network::GameInfo game_info; |
| | game_info.name = name; |
| | game_info.id = params.program_id; |
| | game_info.version = title_version; |
| | room_member->SendGameInfo(game_info); |
| | } |
| |
|
| | status = SystemResultStatus::Success; |
| | return status; |
| | } |
| |
|
| | void ShutdownMainProcess() { |
| | SetShuttingDown(true); |
| |
|
| | |
| | if (perf_stats) { |
| | const auto perf_results = GetAndResetPerfStats(); |
| | constexpr auto performance = Common::Telemetry::FieldType::Performance; |
| |
|
| | telemetry_session->AddField(performance, "Shutdown_EmulationSpeed", |
| | perf_results.emulation_speed * 100.0); |
| | telemetry_session->AddField(performance, "Shutdown_Framerate", |
| | perf_results.average_game_fps); |
| | telemetry_session->AddField(performance, "Shutdown_Frametime", |
| | perf_results.frametime * 1000.0); |
| | telemetry_session->AddField(performance, "Mean_Frametime_MS", |
| | perf_stats->GetMeanFrametime()); |
| | } |
| |
|
| | is_powered_on = false; |
| | exit_locked = false; |
| | exit_requested = false; |
| |
|
| | if (gpu_core != nullptr) { |
| | gpu_core->NotifyShutdown(); |
| | } |
| |
|
| | stop_event.request_stop(); |
| | core_timing.SyncPause(false); |
| | Network::CancelPendingSocketOperations(); |
| | kernel.SuspendEmulation(true); |
| | kernel.CloseServices(); |
| | kernel.ShutdownCores(); |
| | applet_manager.Reset(); |
| | services.reset(); |
| | service_manager.reset(); |
| | fs_controller.Reset(); |
| | cheat_engine.reset(); |
| | telemetry_session.reset(); |
| | core_timing.ClearPendingEvents(); |
| | app_loader.reset(); |
| | audio_core.reset(); |
| | gpu_core.reset(); |
| | host1x_core.reset(); |
| | perf_stats.reset(); |
| | cpu_manager.Shutdown(); |
| | debugger.reset(); |
| | kernel.Shutdown(); |
| | stop_event = {}; |
| | Network::RestartSocketOperations(); |
| |
|
| | if (auto room_member = room_network.GetRoomMember().lock()) { |
| | Network::GameInfo game_info{}; |
| | room_member->SendGameInfo(game_info); |
| | } |
| |
|
| | LOG_DEBUG(Core, "Shutdown OK"); |
| | } |
| |
|
| | bool IsShuttingDown() const { |
| | return is_shutting_down; |
| | } |
| |
|
| | void SetShuttingDown(bool shutting_down) { |
| | is_shutting_down = shutting_down; |
| | } |
| |
|
| | Loader::ResultStatus GetGameName(std::string& out) const { |
| | if (app_loader == nullptr) |
| | return Loader::ResultStatus::ErrorNotInitialized; |
| | return app_loader->ReadTitle(out); |
| | } |
| |
|
| | void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::KProcess& process) { |
| | std::vector<u8> nacp_data; |
| | FileSys::NACP nacp; |
| | if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) { |
| | nacp_data = nacp.GetRawBytes(); |
| | } else { |
| | nacp_data.resize(sizeof(FileSys::RawNACP)); |
| | } |
| |
|
| | Service::Glue::ApplicationLaunchProperty launch{}; |
| | launch.title_id = process.GetProgramId(); |
| |
|
| | FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider}; |
| | launch.version = pm.GetGameVersion().value_or(0); |
| |
|
| | |
| | |
| | launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry( |
| | launch.title_id, FileSys::ContentRecordType::Program)); |
| | launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry( |
| | FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program)); |
| |
|
| | arp_manager.Register(launch.title_id, launch, std::move(nacp_data)); |
| | } |
| |
|
| | void SetStatus(SystemResultStatus new_status, const char* details = nullptr) { |
| | status = new_status; |
| | if (details) { |
| | status_details = details; |
| | } |
| | } |
| |
|
| | PerfStatsResults GetAndResetPerfStats() { |
| | return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs()); |
| | } |
| |
|
| | mutable std::mutex suspend_guard; |
| | std::atomic_bool is_paused{}; |
| | std::atomic<bool> is_shutting_down{}; |
| |
|
| | Timing::CoreTiming core_timing; |
| | Kernel::KernelCore kernel; |
| | |
| | FileSys::VirtualFilesystem virtual_filesystem; |
| | |
| | std::unique_ptr<FileSys::ContentProviderUnion> content_provider; |
| | Service::FileSystem::FileSystemController fs_controller; |
| | |
| | std::unique_ptr<Loader::AppLoader> app_loader; |
| | std::unique_ptr<Tegra::GPU> gpu_core; |
| | std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; |
| | std::unique_ptr<Core::DeviceMemory> device_memory; |
| | std::unique_ptr<AudioCore::AudioCore> audio_core; |
| | Core::HID::HIDCore hid_core; |
| | Network::RoomNetwork room_network; |
| |
|
| | CpuManager cpu_manager; |
| | std::atomic_bool is_powered_on{}; |
| | bool exit_locked = false; |
| | bool exit_requested = false; |
| |
|
| | bool nvdec_active{}; |
| |
|
| | Reporter reporter; |
| | std::unique_ptr<Memory::CheatEngine> cheat_engine; |
| | std::unique_ptr<Tools::Freezer> memory_freezer; |
| | std::array<u8, 0x20> build_id{}; |
| |
|
| | std::unique_ptr<Tools::RenderdocAPI> renderdoc_api; |
| |
|
| | |
| | Service::AM::AppletManager applet_manager; |
| | Service::AM::Frontend::FrontendAppletHolder frontend_applets; |
| |
|
| | |
| | Service::APM::Controller apm_controller{core_timing}; |
| |
|
| | |
| | Service::Glue::ARPManager arp_manager; |
| | Service::Account::ProfileManager profile_manager; |
| |
|
| | |
| | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| |
|
| | |
| | std::unique_ptr<Service::Services> services; |
| |
|
| | |
| | std::unique_ptr<Core::TelemetrySession> telemetry_session; |
| |
|
| | |
| | Network::NetworkInstance network_instance; |
| |
|
| | |
| | std::unique_ptr<Core::Debugger> debugger; |
| |
|
| | SystemResultStatus status = SystemResultStatus::Success; |
| | std::string status_details = ""; |
| |
|
| | std::unique_ptr<Core::PerfStats> perf_stats; |
| | Core::SpeedLimiter speed_limiter; |
| |
|
| | bool is_multicore{}; |
| | bool is_async_gpu{}; |
| | bool extended_memory_layout{}; |
| |
|
| | ExecuteProgramCallback execute_program_callback; |
| | ExitCallback exit_callback; |
| | std::stop_source stop_event; |
| |
|
| | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; |
| | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; |
| |
|
| | std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> |
| | gpu_dirty_memory_managers; |
| |
|
| | std::deque<std::vector<u8>> user_channel; |
| | }; |
| |
|
| | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| |
|
| | System::~System() = default; |
| |
|
| | CpuManager& System::GetCpuManager() { |
| | return impl->cpu_manager; |
| | } |
| |
|
| | const CpuManager& System::GetCpuManager() const { |
| | return impl->cpu_manager; |
| | } |
| |
|
| | void System::Initialize() { |
| | impl->Initialize(*this); |
| | } |
| |
|
| | void System::Run() { |
| | impl->Run(); |
| | } |
| |
|
| | void System::Pause() { |
| | impl->Pause(); |
| | } |
| |
|
| | bool System::IsPaused() const { |
| | return impl->IsPaused(); |
| | } |
| |
|
| | void System::ShutdownMainProcess() { |
| | impl->ShutdownMainProcess(); |
| | } |
| |
|
| | bool System::IsShuttingDown() const { |
| | return impl->IsShuttingDown(); |
| | } |
| |
|
| | void System::SetShuttingDown(bool shutting_down) { |
| | impl->SetShuttingDown(shutting_down); |
| | } |
| |
|
| | void System::DetachDebugger() { |
| | if (impl->debugger) { |
| | impl->debugger->NotifyShutdown(); |
| | } |
| | } |
| |
|
| | std::unique_lock<std::mutex> System::StallApplication() { |
| | return impl->StallApplication(); |
| | } |
| |
|
| | void System::UnstallApplication() { |
| | impl->UnstallApplication(); |
| | } |
| |
|
| | void System::SetNVDECActive(bool is_nvdec_active) { |
| | impl->SetNVDECActive(is_nvdec_active); |
| | } |
| |
|
| | bool System::GetNVDECActive() { |
| | return impl->GetNVDECActive(); |
| | } |
| |
|
| | void System::InitializeDebugger() { |
| | impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); |
| | } |
| |
|
| | SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, |
| | Service::AM::FrontendAppletParameters& params) { |
| | return impl->Load(*this, emu_window, filepath, params); |
| | } |
| |
|
| | bool System::IsPoweredOn() const { |
| | return impl->is_powered_on.load(std::memory_order::relaxed); |
| | } |
| |
|
| | void System::PrepareReschedule(const u32 core_index) { |
| | impl->kernel.PrepareReschedule(core_index); |
| | } |
| |
|
| | size_t System::GetCurrentHostThreadID() const { |
| | return impl->kernel.GetCurrentHostThreadID(); |
| | } |
| |
|
| | std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager() { |
| | return impl->gpu_dirty_memory_managers; |
| | } |
| |
|
| | void System::GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback) { |
| | for (auto& manager : impl->gpu_dirty_memory_managers) { |
| | manager.Gather(callback); |
| | } |
| | } |
| |
|
| | PerfStatsResults System::GetAndResetPerfStats() { |
| | return impl->GetAndResetPerfStats(); |
| | } |
| |
|
| | TelemetrySession& System::TelemetrySession() { |
| | return *impl->telemetry_session; |
| | } |
| |
|
| | const TelemetrySession& System::TelemetrySession() const { |
| | return *impl->telemetry_session; |
| | } |
| |
|
| | Kernel::PhysicalCore& System::CurrentPhysicalCore() { |
| | return impl->kernel.CurrentPhysicalCore(); |
| | } |
| |
|
| | const Kernel::PhysicalCore& System::CurrentPhysicalCore() const { |
| | return impl->kernel.CurrentPhysicalCore(); |
| | } |
| |
|
| | |
| | Kernel::GlobalSchedulerContext& System::GlobalSchedulerContext() { |
| | return impl->kernel.GlobalSchedulerContext(); |
| | } |
| |
|
| | |
| | const Kernel::GlobalSchedulerContext& System::GlobalSchedulerContext() const { |
| | return impl->kernel.GlobalSchedulerContext(); |
| | } |
| |
|
| | Kernel::KProcess* System::ApplicationProcess() { |
| | return impl->kernel.ApplicationProcess(); |
| | } |
| |
|
| | Core::DeviceMemory& System::DeviceMemory() { |
| | return *impl->device_memory; |
| | } |
| |
|
| | const Core::DeviceMemory& System::DeviceMemory() const { |
| | return *impl->device_memory; |
| | } |
| |
|
| | const Kernel::KProcess* System::ApplicationProcess() const { |
| | return impl->kernel.ApplicationProcess(); |
| | } |
| |
|
| | Memory::Memory& System::ApplicationMemory() { |
| | return impl->kernel.ApplicationProcess()->GetMemory(); |
| | } |
| |
|
| | const Core::Memory::Memory& System::ApplicationMemory() const { |
| | return impl->kernel.ApplicationProcess()->GetMemory(); |
| | } |
| |
|
| | Tegra::GPU& System::GPU() { |
| | return *impl->gpu_core; |
| | } |
| |
|
| | const Tegra::GPU& System::GPU() const { |
| | return *impl->gpu_core; |
| | } |
| |
|
| | Tegra::Host1x::Host1x& System::Host1x() { |
| | return *impl->host1x_core; |
| | } |
| |
|
| | const Tegra::Host1x::Host1x& System::Host1x() const { |
| | return *impl->host1x_core; |
| | } |
| |
|
| | VideoCore::RendererBase& System::Renderer() { |
| | return impl->gpu_core->Renderer(); |
| | } |
| |
|
| | const VideoCore::RendererBase& System::Renderer() const { |
| | return impl->gpu_core->Renderer(); |
| | } |
| |
|
| | Kernel::KernelCore& System::Kernel() { |
| | return impl->kernel; |
| | } |
| |
|
| | const Kernel::KernelCore& System::Kernel() const { |
| | return impl->kernel; |
| | } |
| |
|
| | HID::HIDCore& System::HIDCore() { |
| | return impl->hid_core; |
| | } |
| |
|
| | const HID::HIDCore& System::HIDCore() const { |
| | return impl->hid_core; |
| | } |
| |
|
| | AudioCore::AudioCore& System::AudioCore() { |
| | return *impl->audio_core; |
| | } |
| |
|
| | const AudioCore::AudioCore& System::AudioCore() const { |
| | return *impl->audio_core; |
| | } |
| |
|
| | Timing::CoreTiming& System::CoreTiming() { |
| | return impl->core_timing; |
| | } |
| |
|
| | const Timing::CoreTiming& System::CoreTiming() const { |
| | return impl->core_timing; |
| | } |
| |
|
| | Core::PerfStats& System::GetPerfStats() { |
| | return *impl->perf_stats; |
| | } |
| |
|
| | const Core::PerfStats& System::GetPerfStats() const { |
| | return *impl->perf_stats; |
| | } |
| |
|
| | Core::SpeedLimiter& System::SpeedLimiter() { |
| | return impl->speed_limiter; |
| | } |
| |
|
| | const Core::SpeedLimiter& System::SpeedLimiter() const { |
| | return impl->speed_limiter; |
| | } |
| |
|
| | u64 System::GetApplicationProcessProgramID() const { |
| | return impl->kernel.ApplicationProcess()->GetProgramId(); |
| | } |
| |
|
| | Loader::ResultStatus System::GetGameName(std::string& out) const { |
| | return impl->GetGameName(out); |
| | } |
| |
|
| | void System::SetStatus(SystemResultStatus new_status, const char* details) { |
| | impl->SetStatus(new_status, details); |
| | } |
| |
|
| | const std::string& System::GetStatusDetails() const { |
| | return impl->status_details; |
| | } |
| |
|
| | Loader::AppLoader& System::GetAppLoader() { |
| | return *impl->app_loader; |
| | } |
| |
|
| | const Loader::AppLoader& System::GetAppLoader() const { |
| | return *impl->app_loader; |
| | } |
| |
|
| | void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { |
| | impl->virtual_filesystem = std::move(vfs); |
| | } |
| |
|
| | FileSys::VirtualFilesystem System::GetFilesystem() const { |
| | return impl->virtual_filesystem; |
| | } |
| |
|
| | void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list, |
| | const std::array<u8, 32>& build_id, u64 main_region_begin, |
| | u64 main_region_size) { |
| | impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id); |
| | impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size); |
| | } |
| |
|
| | void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { |
| | impl->frontend_applets.SetFrontendAppletSet(std::move(set)); |
| | } |
| |
|
| | Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { |
| | return impl->frontend_applets; |
| | } |
| |
|
| | const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { |
| | return impl->frontend_applets; |
| | } |
| |
|
| | Service::AM::AppletManager& System::GetAppletManager() { |
| | return impl->applet_manager; |
| | } |
| |
|
| | void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { |
| | impl->content_provider = std::move(provider); |
| | } |
| |
|
| | FileSys::ContentProvider& System::GetContentProvider() { |
| | return *impl->content_provider; |
| | } |
| |
|
| | const FileSys::ContentProvider& System::GetContentProvider() const { |
| | return *impl->content_provider; |
| | } |
| |
|
| | FileSys::ContentProviderUnion& System::GetContentProviderUnion() { |
| | return *impl->content_provider; |
| | } |
| |
|
| | const FileSys::ContentProviderUnion& System::GetContentProviderUnion() const { |
| | return *impl->content_provider; |
| | } |
| |
|
| | Service::FileSystem::FileSystemController& System::GetFileSystemController() { |
| | return impl->fs_controller; |
| | } |
| |
|
| | const Service::FileSystem::FileSystemController& System::GetFileSystemController() const { |
| | return impl->fs_controller; |
| | } |
| |
|
| | void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, |
| | FileSys::ContentProvider* provider) { |
| | impl->content_provider->SetSlot(slot, provider); |
| | } |
| |
|
| | void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { |
| | impl->content_provider->ClearSlot(slot); |
| | } |
| |
|
| | const Reporter& System::GetReporter() const { |
| | return impl->reporter; |
| | } |
| |
|
| | Service::Glue::ARPManager& System::GetARPManager() { |
| | return impl->arp_manager; |
| | } |
| |
|
| | const Service::Glue::ARPManager& System::GetARPManager() const { |
| | return impl->arp_manager; |
| | } |
| |
|
| | Service::APM::Controller& System::GetAPMController() { |
| | return impl->apm_controller; |
| | } |
| |
|
| | const Service::APM::Controller& System::GetAPMController() const { |
| | return impl->apm_controller; |
| | } |
| |
|
| | Service::Account::ProfileManager& System::GetProfileManager() { |
| | return impl->profile_manager; |
| | } |
| |
|
| | const Service::Account::ProfileManager& System::GetProfileManager() const { |
| | return impl->profile_manager; |
| | } |
| |
|
| | void System::SetExitLocked(bool locked) { |
| | impl->exit_locked = locked; |
| | } |
| |
|
| | bool System::GetExitLocked() const { |
| | return impl->exit_locked; |
| | } |
| |
|
| | void System::SetExitRequested(bool requested) { |
| | impl->exit_requested = requested; |
| | } |
| |
|
| | bool System::GetExitRequested() const { |
| | return impl->exit_requested; |
| | } |
| |
|
| | void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { |
| | impl->build_id = id; |
| | } |
| |
|
| | const System::CurrentBuildProcessID& System::GetApplicationProcessBuildID() const { |
| | return impl->build_id; |
| | } |
| |
|
| | Service::SM::ServiceManager& System::ServiceManager() { |
| | return *impl->service_manager; |
| | } |
| |
|
| | const Service::SM::ServiceManager& System::ServiceManager() const { |
| | return *impl->service_manager; |
| | } |
| |
|
| | void System::RegisterCoreThread(std::size_t id) { |
| | impl->kernel.RegisterCoreThread(id); |
| | } |
| |
|
| | void System::RegisterHostThread() { |
| | impl->kernel.RegisterHostThread(); |
| | } |
| |
|
| | void System::EnterCPUProfile() { |
| | std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| | impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_cpu[core]); |
| | } |
| |
|
| | void System::ExitCPUProfile() { |
| | std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| | MicroProfileLeave(impl->microprofile_cpu[core], impl->dynarmic_ticks[core]); |
| | } |
| |
|
| | bool System::IsMulticore() const { |
| | return impl->is_multicore; |
| | } |
| |
|
| | bool System::DebuggerEnabled() const { |
| | return Settings::values.use_gdbstub.GetValue(); |
| | } |
| |
|
| | Core::Debugger& System::GetDebugger() { |
| | return *impl->debugger; |
| | } |
| |
|
| | const Core::Debugger& System::GetDebugger() const { |
| | return *impl->debugger; |
| | } |
| |
|
| | Network::RoomNetwork& System::GetRoomNetwork() { |
| | return impl->room_network; |
| | } |
| |
|
| | const Network::RoomNetwork& System::GetRoomNetwork() const { |
| | return impl->room_network; |
| | } |
| |
|
| | Tools::RenderdocAPI& System::GetRenderdocAPI() { |
| | return *impl->renderdoc_api; |
| | } |
| |
|
| | void System::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { |
| | return impl->kernel.RunServer(std::move(server_manager)); |
| | } |
| |
|
| | void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { |
| | impl->execute_program_callback = std::move(callback); |
| | } |
| |
|
| | void System::ExecuteProgram(std::size_t program_index) { |
| | if (impl->execute_program_callback) { |
| | impl->execute_program_callback(program_index); |
| | } else { |
| | LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend"); |
| | } |
| | } |
| |
|
| | std::deque<std::vector<u8>>& System::GetUserChannel() { |
| | return impl->user_channel; |
| | } |
| |
|
| | void System::RegisterExitCallback(ExitCallback&& callback) { |
| | impl->exit_callback = std::move(callback); |
| | } |
| |
|
| | void System::Exit() { |
| | if (impl->exit_callback) { |
| | impl->exit_callback(); |
| | } else { |
| | LOG_CRITICAL(Core, "exit_callback must be initialized by the frontend"); |
| | } |
| | } |
| |
|
| | void System::ApplySettings() { |
| | impl->RefreshTime(*this); |
| |
|
| | if (IsPoweredOn()) { |
| | Renderer().RefreshBaseSettings(); |
| | } |
| | } |
| |
|
| | } |
| |
|