mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									f346949989
								
							
						
					
					
						commit
						a177769c3b
					
				
					 9 changed files with 87 additions and 6 deletions
				
			
		|  | @ -452,6 +452,7 @@ void Config::ReadCoreValues() { | ||||||
| 
 | 
 | ||||||
|     if (global) { |     if (global) { | ||||||
|         ReadBasicSetting(Settings::values.use_cpu_jit); |         ReadBasicSetting(Settings::values.use_cpu_jit); | ||||||
|  |         ReadBasicSetting(Settings::values.delay_start_for_lle_modules); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
|  | @ -979,6 +980,7 @@ void Config::SaveCoreValues() { | ||||||
| 
 | 
 | ||||||
|     if (global) { |     if (global) { | ||||||
|         WriteBasicSetting(Settings::values.use_cpu_jit); |         WriteBasicSetting(Settings::values.use_cpu_jit); | ||||||
|  |         WriteBasicSetting(Settings::values.delay_start_for_lle_modules); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
|  |  | ||||||
|  | @ -94,6 +94,8 @@ void ConfigureDebug::SetConfiguration() { | ||||||
|     ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); |     ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); | ||||||
|     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.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->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_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); | ||||||
|     ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.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()); |     filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||||
|     Common::Log::SetGlobalFilter(filter); |     Common::Log::SetGlobalFilter(filter); | ||||||
|     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); |     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.renderer_debug = ui->toggle_renderer_debug->isChecked(); | ||||||
|     Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked(); |     Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -219,6 +219,25 @@ | ||||||
|      </layout> |      </layout> | ||||||
|     </widget> |     </widget> | ||||||
|    </item> |    </item> | ||||||
|  |     <item> | ||||||
|  |       <widget class="QGroupBox" name="groupBox_5"> | ||||||
|  |         <property name="title"> | ||||||
|  |           <string>Miscellaneus</string> | ||||||
|  |         </property> | ||||||
|  |         <layout class="QGridLayout" name="gridLayout_2"> | ||||||
|  |           <item row="1" column="0"> | ||||||
|  |             <widget class="QCheckBox" name="delay_start_for_lle_modules"> | ||||||
|  |               <property name="toolTip"> | ||||||
|  |                 <string><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></string> | ||||||
|  |               </property> | ||||||
|  |               <property name="text"> | ||||||
|  |                 <string>Delay app start for LLE module initialization</string> | ||||||
|  |               </property> | ||||||
|  |             </widget> | ||||||
|  |           </item> | ||||||
|  |         </layout> | ||||||
|  |       </widget> | ||||||
|  |     </item> | ||||||
|    <item> |    <item> | ||||||
|     <widget class="QLabel" name="label_cpu_clock_info"> |     <widget class="QLabel" name="label_cpu_clock_info"> | ||||||
|      <property name="text"> |      <property name="text"> | ||||||
|  |  | ||||||
|  | @ -141,6 +141,7 @@ void LogSettings() { | ||||||
|     log_setting("System_RegionValue", values.region_value.GetValue()); |     log_setting("System_RegionValue", values.region_value.GetValue()); | ||||||
|     log_setting("System_PluginLoader", values.plugin_loader_enabled.GetValue()); |     log_setting("System_PluginLoader", values.plugin_loader_enabled.GetValue()); | ||||||
|     log_setting("System_PluginLoaderAllowed", values.allow_plugin_loader.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_UseGdbstub", values.use_gdbstub.GetValue()); | ||||||
|     log_setting("Debugging_GdbstubPort", values.gdbstub_port.GetValue()); |     log_setting("Debugging_GdbstubPort", values.gdbstub_port.GetValue()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -529,6 +529,7 @@ struct Values { | ||||||
|     // Debugging
 |     // Debugging
 | ||||||
|     bool record_frame_times; |     bool record_frame_times; | ||||||
|     std::unordered_map<std::string, bool> lle_modules; |     std::unordered_map<std::string, bool> lle_modules; | ||||||
|  |     Setting<bool> delay_start_for_lle_modules{true, "delay_start_for_lle_modules"}; | ||||||
|     Setting<bool> use_gdbstub{false, "use_gdbstub"}; |     Setting<bool> use_gdbstub{false, "use_gdbstub"}; | ||||||
|     Setting<u16> gdbstub_port{24689, "gdbstub_port"}; |     Setting<u16> gdbstub_port{24689, "gdbstub_port"}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -183,6 +183,7 @@ void KernelSystem::serialize(Archive& ar, const unsigned int file_version) { | ||||||
|     ar& next_thread_id; |     ar& next_thread_id; | ||||||
|     ar& memory_mode; |     ar& memory_mode; | ||||||
|     ar& n3ds_hw_caps; |     ar& n3ds_hw_caps; | ||||||
|  |     ar& main_thread_extended_sleep; | ||||||
|     // Deliberately don't include debugger info to allow debugging through loads
 |     // Deliberately don't include debugger info to allow debugging through loads
 | ||||||
| 
 | 
 | ||||||
|     if (Archive::is_loading::value) { |     if (Archive::is_loading::value) { | ||||||
|  |  | ||||||
|  | @ -185,12 +185,14 @@ public: | ||||||
|      * @param processor_id The ID(s) of the processors on which the thread is desired to be run |      * @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 stack_top The address of the thread's stack top | ||||||
|      * @param owner_process The parent process for the thread |      * @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 |      * @return A shared pointer to the newly created thread | ||||||
|      */ |      */ | ||||||
|     ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point, |     ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point, | ||||||
|                                                     u32 priority, u32 arg, s32 processor_id, |                                                     u32 priority, u32 arg, s32 processor_id, | ||||||
|                                                     VAddr stack_top, |                                                     VAddr stack_top, | ||||||
|                                                     std::shared_ptr<Process> owner_process); |                                                     std::shared_ptr<Process> owner_process, | ||||||
|  |                                                     bool make_ready = true); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Creates a semaphore. |      * Creates a semaphore. | ||||||
|  | @ -338,6 +340,15 @@ public: | ||||||
| 
 | 
 | ||||||
|     Core::Timing& timing; |     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: | private: | ||||||
|     void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, u64 override_init_time); |     void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, u64 override_init_time); | ||||||
| 
 | 
 | ||||||
|  | @ -386,6 +397,14 @@ private: | ||||||
|      */ |      */ | ||||||
|     std::recursive_mutex hle_lock; |     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; |     friend class boost::serialization::access; | ||||||
|     template <class Archive> |     template <class Archive> | ||||||
|     void serialize(Archive& ar, const unsigned int file_version); |     void serialize(Archive& ar, const unsigned int file_version); | ||||||
|  |  | ||||||
|  | @ -10,8 +10,10 @@ | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/serialization/boost_flat_set.h" | #include "common/serialization/boost_flat_set.h" | ||||||
|  | #include "common/settings.h" | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/arm/skyeye_common/armstate.h" | #include "core/arm/skyeye_common/armstate.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "core/hle/kernel/errors.h" | #include "core/hle/kernel/errors.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/mutex.h" | #include "core/hle/kernel/mutex.h" | ||||||
|  | @ -324,7 +326,7 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, u32 | ||||||
| 
 | 
 | ||||||
| ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread( | ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread( | ||||||
|     std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top, |     std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top, | ||||||
|     std::shared_ptr<Process> owner_process) { |     std::shared_ptr<Process> owner_process, bool make_ready) { | ||||||
|     // Check if priority is in ranged. Lowest priority -> highest priority id.
 |     // Check if priority is in ranged. Lowest priority -> highest priority id.
 | ||||||
|     if (priority > ThreadPrioLowest) { |     if (priority > ThreadPrioLowest) { | ||||||
|         LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); |         LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); | ||||||
|  | @ -367,8 +369,11 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread( | ||||||
|     // to initialize the context
 |     // to initialize the context
 | ||||||
|     ResetThreadContext(thread->context, stack_top, entry_point, arg); |     ResetThreadContext(thread->context, stack_top, entry_point, arg); | ||||||
| 
 | 
 | ||||||
|     thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get()); |     if (make_ready) { | ||||||
|  |         thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, | ||||||
|  |                                                              thread.get()); | ||||||
|         thread->status = ThreadStatus::Ready; |         thread->status = ThreadStatus::Ready; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return thread; |     return thread; | ||||||
| } | } | ||||||
|  | @ -405,16 +410,36 @@ void Thread::BoostPriority(u32 priority) { | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, | std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, | ||||||
|                                         std::shared_ptr<Process> owner_process) { |                                         std::shared_ptr<Process> 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<u32>(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
 |     // Initialize new "main" thread
 | ||||||
|     auto thread_res = |     auto thread_res = | ||||||
|         kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor, |         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> thread = std::move(thread_res).Unwrap(); |     std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); | ||||||
| 
 | 
 | ||||||
|     thread->context.fpscr = |     thread->context.fpscr = | ||||||
|         FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
 |         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.
 |     // Note: The newly created thread will be run when the scheduler fires.
 | ||||||
|     return thread; |     return thread; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -214,11 +214,21 @@ static bool AttemptLLE(const ServiceModuleInfo& service_module) { | ||||||
| /// Initialize ServiceManager
 | /// Initialize ServiceManager
 | ||||||
| void Init(Core::System& core) { | void Init(Core::System& core) { | ||||||
|     SM::ServiceManager::InstallInterfaces(core); |     SM::ServiceManager::InstallInterfaces(core); | ||||||
|  |     core.Kernel().SetAppMainThreadExtendedSleep(false); | ||||||
|  |     bool lle_module_present = false; | ||||||
| 
 | 
 | ||||||
|     for (const auto& service_module : service_module_map) { |     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); |             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"); |     LOG_DEBUG(Service, "initialized OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue