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
|
@ -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->status = ThreadStatus::Ready;
|
thread_managers[processor_id]->ready_queue.push_back(thread->current_priority,
|
||||||
|
thread.get());
|
||||||
|
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,10 +214,20 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue