From a177769c3be8f82a7d1075596338a013ad1236ab Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Fri, 12 Jan 2024 21:48:00 +0100 Subject: [PATCH] Add random sleep to game main thread on first boot when using LLE modules (#7199) * Add random delay to app main thread * Suggestions * Remove randomness, only delay with lle * Apply suggestions * Fix clang format * Fix compilation (again) * Remove unused include --- src/citra_qt/configuration/config.cpp | 2 ++ .../configuration/configure_debug.cpp | 3 ++ src/citra_qt/configuration/configure_debug.ui | 19 +++++++++++ src/common/settings.cpp | 1 + src/common/settings.h | 1 + src/core/hle/kernel/kernel.cpp | 1 + src/core/hle/kernel/kernel.h | 21 +++++++++++- src/core/hle/kernel/thread.cpp | 33 ++++++++++++++++--- src/core/hle/service/service.cpp | 12 ++++++- 9 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 74d779b0a9..58495ae0cd 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -452,6 +452,7 @@ void Config::ReadCoreValues() { if (global) { ReadBasicSetting(Settings::values.use_cpu_jit); + ReadBasicSetting(Settings::values.delay_start_for_lle_modules); } qt_config->endGroup(); @@ -979,6 +980,7 @@ void Config::SaveCoreValues() { if (global) { WriteBasicSetting(Settings::values.use_cpu_jit); + WriteBasicSetting(Settings::values.delay_start_for_lle_modules); } qt_config->endGroup(); diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index 98d95c9e03..9143959e3b 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -94,6 +94,8 @@ void ConfigureDebug::SetConfiguration() { ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); + ui->delay_start_for_lle_modules->setChecked( + Settings::values.delay_start_for_lle_modules.GetValue()); ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue()); @@ -125,6 +127,7 @@ void ConfigureDebug::ApplyConfiguration() { filter.ParseFilterString(Settings::values.log_filter.GetValue()); Common::Log::SetGlobalFilter(filter); Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); + Settings::values.delay_start_for_lle_modules = ui->delay_start_for_lle_modules->isChecked(); Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked(); diff --git a/src/citra_qt/configuration/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui index 20b84441cb..faf8b9ee0e 100644 --- a/src/citra_qt/configuration/configure_debug.ui +++ b/src/citra_qt/configuration/configure_debug.ui @@ -219,6 +219,25 @@ + + + + Miscellaneus + + + + + + <html><head/><body><p>Introduces a delay to the first ever launched app thread if LLE modules are enabled, to allow them to initialize.</p></body></html> + + + Delay app start for LLE module initialization + + + + + + diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 486340a3fe..e7e3d59f15 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -141,6 +141,7 @@ void LogSettings() { log_setting("System_RegionValue", values.region_value.GetValue()); log_setting("System_PluginLoader", values.plugin_loader_enabled.GetValue()); log_setting("System_PluginLoaderAllowed", values.allow_plugin_loader.GetValue()); + log_setting("Debugging_DelayStartForLLEModules", values.delay_start_for_lle_modules.GetValue()); log_setting("Debugging_UseGdbstub", values.use_gdbstub.GetValue()); log_setting("Debugging_GdbstubPort", values.gdbstub_port.GetValue()); } diff --git a/src/common/settings.h b/src/common/settings.h index 3b51e5dd45..8fc0f36c42 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -529,6 +529,7 @@ struct Values { // Debugging bool record_frame_times; std::unordered_map lle_modules; + Setting delay_start_for_lle_modules{true, "delay_start_for_lle_modules"}; Setting use_gdbstub{false, "use_gdbstub"}; Setting gdbstub_port{24689, "gdbstub_port"}; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index db0caf59c5..9df6e75ddb 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -183,6 +183,7 @@ void KernelSystem::serialize(Archive& ar, const unsigned int file_version) { ar& next_thread_id; ar& memory_mode; ar& n3ds_hw_caps; + ar& main_thread_extended_sleep; // Deliberately don't include debugger info to allow debugging through loads if (Archive::is_loading::value) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index e9f77be6c2..8c3f2b35c8 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -185,12 +185,14 @@ public: * @param processor_id The ID(s) of the processors on which the thread is desired to be run * @param stack_top The address of the thread's stack top * @param owner_process The parent process for the thread + * @param make_ready If the thread should be put in the ready queue * @return A shared pointer to the newly created thread */ ResultVal> CreateThread(std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top, - std::shared_ptr owner_process); + std::shared_ptr owner_process, + bool make_ready = true); /** * Creates a semaphore. @@ -338,6 +340,15 @@ public: Core::Timing& timing; + /// Sleep main thread of the first ever launched non-sysmodule process. + void SetAppMainThreadExtendedSleep(bool requires_sleep) { + main_thread_extended_sleep = requires_sleep; + } + + bool GetAppMainThreadExtendedSleep() const { + return main_thread_extended_sleep; + } + private: void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, u64 override_init_time); @@ -386,6 +397,14 @@ private: */ std::recursive_mutex hle_lock; + /* + * Flags non system module main threads to wait a bit before running. On real hardware, + * system modules have plenty of time to load before the game is loaded, but on citra they + * start at the same time as the game. The artificial wait gives system modules some time + * to load and setup themselves before the game starts. + */ + bool main_thread_extended_sleep = false; + friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int file_version); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 83a2ba8f76..0bcca61772 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -10,8 +10,10 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/serialization/boost_flat_set.h" +#include "common/settings.h" #include "core/arm/arm_interface.h" #include "core/arm/skyeye_common/armstate.h" +#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" @@ -324,7 +326,7 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, u32 ResultVal> KernelSystem::CreateThread( std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top, - std::shared_ptr owner_process) { + std::shared_ptr owner_process, bool make_ready) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > ThreadPrioLowest) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); @@ -367,8 +369,11 @@ ResultVal> KernelSystem::CreateThread( // to initialize the context ResetThreadContext(thread->context, stack_top, entry_point, arg); - thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get()); - thread->status = ThreadStatus::Ready; + if (make_ready) { + thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, + thread.get()); + thread->status = ThreadStatus::Ready; + } return thread; } @@ -405,16 +410,36 @@ void Thread::BoostPriority(u32 priority) { std::shared_ptr SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, std::shared_ptr owner_process) { + + constexpr s64 sleep_app_thread_ns = 2'600'000'000LL; + constexpr u32 system_module_tid_high = 0x00040130; + + const bool is_lle_service = + static_cast(owner_process->codeset->program_id >> 32) == system_module_tid_high; + + s64 sleep_time_ns = 0; + if (!is_lle_service && kernel.GetAppMainThreadExtendedSleep()) { + if (Settings::values.delay_start_for_lle_modules) { + sleep_time_ns = sleep_app_thread_ns; + } + kernel.SetAppMainThreadExtendedSleep(false); + } + // Initialize new "main" thread auto thread_res = kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor, - Memory::HEAP_VADDR_END, owner_process); + Memory::HEAP_VADDR_END, owner_process, sleep_time_ns == 0); std::shared_ptr thread = std::move(thread_res).Unwrap(); thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 + if (sleep_time_ns != 0) { + thread->status = ThreadStatus::WaitSleep; + thread->WakeAfterDelay(sleep_time_ns); + } + // Note: The newly created thread will be run when the scheduler fires. return thread; } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 118ea32dd1..133bfce209 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -214,10 +214,20 @@ static bool AttemptLLE(const ServiceModuleInfo& service_module) { /// Initialize ServiceManager void Init(Core::System& core) { SM::ServiceManager::InstallInterfaces(core); + core.Kernel().SetAppMainThreadExtendedSleep(false); + bool lle_module_present = false; for (const auto& service_module : service_module_map) { - if (!AttemptLLE(service_module) && service_module.init_function != nullptr) + const bool has_lle = AttemptLLE(service_module); + if (!has_lle && service_module.init_function != nullptr) { service_module.init_function(core); + } + lle_module_present |= has_lle; + } + if (lle_module_present) { + // If there is at least one LLE module, tell the kernel to + // add a extended sleep to the app main thread (if option enabled). + core.Kernel().SetAppMainThreadExtendedSleep(true); } LOG_DEBUG(Service, "initialized OK"); }