Compare commits
118 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4908a07c20 | ||
|
|
6ebc972c2b | ||
|
|
94652e122d | ||
|
|
e49184e606 | ||
|
|
77d06d5df0 | ||
|
|
e242f16986 | ||
|
|
333ccf23f8 | ||
|
|
1cdddd17d2 | ||
|
|
7cf74abbf5 | ||
|
|
b3fc36d989 | ||
|
|
2a24c415c1 | ||
|
|
2f32133ad5 | ||
|
|
44b3abdfc0 | ||
|
|
156215d1fa | ||
|
|
019ae82c94 | ||
|
|
822259a3f2 | ||
|
|
f0ed11e318 | ||
|
|
935fee18ed | ||
|
|
6aac5d4c27 | ||
|
|
5e732e7aec | ||
|
|
ac0c5be7c0 | ||
|
|
7f965172c5 | ||
|
|
dd72e4dce4 | ||
|
|
a2d73eaa10 | ||
|
|
9fa1cf3e01 | ||
|
|
e05d2a70b2 | ||
|
|
4af413623b | ||
|
|
54eafbaf17 | ||
|
|
5e7e38ac72 | ||
|
|
6497fbfa96 | ||
|
|
7783c0aaef | ||
|
|
ad45963b45 | ||
|
|
37f1c76613 | ||
|
|
f91b6fbbcb | ||
|
|
ccc0a1e621 | ||
|
|
96579e515a | ||
|
|
7276aaf907 | ||
|
|
54ca48e8b7 | ||
|
|
734fb180bb | ||
|
|
ddda6ae776 | ||
|
|
db9320e754 | ||
|
|
26ef76213c | ||
|
|
a515ede2af | ||
|
|
6be730bdcd | ||
|
|
37a8e2a67e | ||
|
|
d52ad96ce3 | ||
|
|
c126b0718c | ||
|
|
e4de1783e1 | ||
|
|
a9d39b6895 | ||
|
|
4d4a234476 | ||
|
|
e51b852aee | ||
|
|
e826e6715a | ||
|
|
755822ceec | ||
|
|
a92dbec962 | ||
|
|
985599e485 | ||
|
|
38f3442ea5 | ||
|
|
2b92d22bda | ||
|
|
9a104e2b60 | ||
|
|
479369db43 | ||
|
|
3c618a3306 | ||
|
|
3592628302 | ||
|
|
04301e1a8a | ||
|
|
4e2fb9e51a | ||
|
|
a2f842ce54 | ||
|
|
280c779898 | ||
|
|
b64d28492a | ||
|
|
eec9aace60 | ||
|
|
1598426493 | ||
|
|
60bdedc7dd | ||
|
|
257d3c9ecf | ||
|
|
9a9e7dd78b | ||
|
|
834c25f4d9 | ||
|
|
a63af9860b | ||
|
|
3f8eb44e7d | ||
|
|
2e8d737a96 | ||
|
|
0d1bdfc1d4 | ||
|
|
d7f6d516ce | ||
|
|
e596fac6ee | ||
|
|
efb5de1c5f | ||
|
|
a2384a18fa | ||
|
|
42697527ba | ||
|
|
e3d156ab0e | ||
|
|
0d9afdedc4 | ||
|
|
894ed14ebc | ||
|
|
3c2a451f47 | ||
|
|
abbea575cf | ||
|
|
2c49a65d2b | ||
|
|
8f4ff06c4c | ||
|
|
316a2dd22a | ||
|
|
4c74761155 | ||
|
|
f3d6e31e78 | ||
|
|
f62c7091a2 | ||
|
|
b0671c7cfa | ||
|
|
beb55cb90e | ||
|
|
e942d97540 | ||
|
|
5dff28290f | ||
|
|
423acf53b7 | ||
|
|
15c721b909 | ||
|
|
2f89456041 | ||
|
|
bc1399204b | ||
|
|
3dc803a430 | ||
|
|
d14b8fc747 | ||
|
|
08c63d5c75 | ||
|
|
07690572f7 | ||
|
|
3239442de6 | ||
|
|
284015dfd7 | ||
|
|
d604edfedf | ||
|
|
178584e56f | ||
|
|
629f9274ac | ||
|
|
13c82d042f | ||
|
|
f412d2027a | ||
|
|
f13fce3953 | ||
|
|
4c18a207a4 | ||
|
|
04daefa488 | ||
|
|
3bd5d4b6f8 | ||
|
|
669a2d2c67 | ||
|
|
f4587c596f | ||
|
|
4dd85f86a8 |
@@ -131,7 +131,7 @@ add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
if (MSVC)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
|
||||
|
||||
# cubeb and boost still make use of deprecated result_of.
|
||||
# boost still makes use of deprecated result_of.
|
||||
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
4
externals/CMakeLists.txt
vendored
4
externals/CMakeLists.txt
vendored
@@ -44,10 +44,6 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
|
||||
# Unicorn
|
||||
add_library(unicorn-headers INTERFACE)
|
||||
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
|
||||
|
||||
# libusb
|
||||
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
|
||||
add_subdirectory(libusb)
|
||||
|
||||
2
externals/cubeb
vendored
2
externals/cubeb
vendored
Submodule externals/cubeb updated: 1d66483ad2...75d9d125ee
18
externals/find-modules/FindUnicorn.cmake
vendored
18
externals/find-modules/FindUnicorn.cmake
vendored
@@ -1,18 +0,0 @@
|
||||
# Exports:
|
||||
# LIBUNICORN_FOUND
|
||||
# LIBUNICORN_INCLUDE_DIR
|
||||
# LIBUNICORN_LIBRARY
|
||||
|
||||
find_path(LIBUNICORN_INCLUDE_DIR
|
||||
unicorn/unicorn.h
|
||||
HINTS $ENV{UNICORNDIR}
|
||||
PATH_SUFFIXES include)
|
||||
|
||||
find_library(LIBUNICORN_LIBRARY
|
||||
NAMES unicorn
|
||||
HINTS $ENV{UNICORNDIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(unicorn DEFAULT_MSG
|
||||
LIBUNICORN_LIBRARY LIBUNICORN_INCLUDE_DIR)
|
||||
mark_as_advanced(LIBUNICORN_INCLUDE_DIR LIBUNICORN_LIBRARY)
|
||||
@@ -227,7 +227,7 @@ struct CallbackStatus {
|
||||
|
||||
// Triggered once every input change
|
||||
struct InputCallback {
|
||||
std::function<void(CallbackStatus)> on_change;
|
||||
std::function<void(const CallbackStatus&)> on_change;
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
@@ -236,14 +236,10 @@ public:
|
||||
virtual ~InputDevice() = default;
|
||||
|
||||
// Request input device to update if necessary
|
||||
virtual void SoftUpdate() {
|
||||
return;
|
||||
}
|
||||
virtual void SoftUpdate() {}
|
||||
|
||||
// Force input device to update data regardless of the current state
|
||||
virtual void ForceUpdate() {
|
||||
return;
|
||||
}
|
||||
virtual void ForceUpdate() {}
|
||||
|
||||
// Sets the function to be triggered when input changes
|
||||
void SetCallback(InputCallback callback_) {
|
||||
@@ -251,7 +247,7 @@ public:
|
||||
}
|
||||
|
||||
// Triggers the function set in the callback
|
||||
void TriggerOnChange(CallbackStatus status) {
|
||||
void TriggerOnChange(const CallbackStatus& status) {
|
||||
if (callback.on_change) {
|
||||
callback.on_change(status);
|
||||
}
|
||||
@@ -266,11 +262,9 @@ class OutputDevice {
|
||||
public:
|
||||
virtual ~OutputDevice() = default;
|
||||
|
||||
virtual void SetLED([[maybe_unused]] LedStatus led_status) {
|
||||
return;
|
||||
}
|
||||
virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {}
|
||||
|
||||
virtual VibrationError SetVibration([[maybe_unused]] VibrationStatus vibration_status) {
|
||||
virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
|
||||
return VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,9 +71,6 @@ static CPUCaps Detect() {
|
||||
else
|
||||
caps.manufacturer = Manufacturer::Unknown;
|
||||
|
||||
u32 family = {};
|
||||
u32 model = {};
|
||||
|
||||
__cpuid(cpu_id, 0x80000000);
|
||||
|
||||
u32 max_ex_fn = cpu_id[0];
|
||||
@@ -84,15 +81,6 @@ static CPUCaps Detect() {
|
||||
// Detect family and other miscellaneous features
|
||||
if (max_std_fn >= 1) {
|
||||
__cpuid(cpu_id, 0x00000001);
|
||||
family = (cpu_id[0] >> 8) & 0xf;
|
||||
model = (cpu_id[0] >> 4) & 0xf;
|
||||
if (family == 0xf) {
|
||||
family += (cpu_id[0] >> 20) & 0xff;
|
||||
}
|
||||
if (family >= 6) {
|
||||
model += ((cpu_id[0] >> 16) & 0xf) << 4;
|
||||
}
|
||||
|
||||
if ((cpu_id[3] >> 25) & 1)
|
||||
caps.sse = true;
|
||||
if ((cpu_id[3] >> 26) & 1)
|
||||
|
||||
@@ -187,6 +187,7 @@ add_library(core STATIC
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
hle/kernel/k_light_lock.h
|
||||
@@ -239,6 +240,7 @@ add_library(core STATIC
|
||||
hle/kernel/k_system_control.h
|
||||
hle/kernel/k_thread.cpp
|
||||
hle/kernel/k_thread.h
|
||||
hle/kernel/k_thread_queue.cpp
|
||||
hle/kernel/k_thread_queue.h
|
||||
hle/kernel/k_trace.h
|
||||
hle/kernel/k_transfer_memory.cpp
|
||||
|
||||
@@ -521,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
|
||||
return impl->kernel.CurrentPhysicalCore().ArmInterface();
|
||||
}
|
||||
|
||||
std::size_t System::CurrentCoreIndex() const {
|
||||
std::size_t core = impl->kernel.GetCurrentHostThreadID();
|
||||
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
|
||||
return core;
|
||||
}
|
||||
|
||||
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
|
||||
return impl->kernel.CurrentPhysicalCore();
|
||||
}
|
||||
|
||||
@@ -208,9 +208,6 @@ public:
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
[[nodiscard]] std::size_t CurrentCoreIndex() const;
|
||||
|
||||
/// Gets the physical core for the CPU core that is currently running
|
||||
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
|
||||
|
||||
|
||||
@@ -117,17 +117,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
kernel.CurrentScheduler()->RescheduleCurrentCore();
|
||||
{
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCoreRunIdleThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
while (true) {
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
physical_core.Idle();
|
||||
kernel.CurrentScheduler()->RescheduleCurrentCore();
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
kernel.CurrentPhysicalCore().Idle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,12 +136,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto core = kernel.CurrentPhysicalCoreIndex();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
@@ -346,13 +347,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
sc_sync_first_use = false;
|
||||
}
|
||||
|
||||
// Abort if emulation was killed before the session really starts
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Emulation was stopped
|
||||
if (stop_token.stop_requested()) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
@@ -66,9 +66,10 @@ void EmulatedConsole::ReloadInput() {
|
||||
|
||||
motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
|
||||
if (motion_devices) {
|
||||
Common::Input::InputCallback motion_callback{
|
||||
[this](Common::Input::CallbackStatus callback) { SetMotion(callback); }};
|
||||
motion_devices->SetCallback(motion_callback);
|
||||
motion_devices->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
|
||||
});
|
||||
}
|
||||
|
||||
// Unique index for identifying touch device source
|
||||
@@ -78,9 +79,12 @@ void EmulatedConsole::ReloadInput() {
|
||||
if (!touch_device) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback touch_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) { SetTouch(callback, index); }};
|
||||
touch_device->SetCallback(touch_callback);
|
||||
touch_device->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetTouch(callback, index);
|
||||
},
|
||||
});
|
||||
index++;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +131,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetMotion(Common::Input::CallbackStatus callback) {
|
||||
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
auto& raw_status = console.motion_values.raw_status;
|
||||
auto& emulated = console.motion_values.emulated;
|
||||
@@ -162,8 +166,7 @@ void EmulatedConsole::SetMotion(Common::Input::CallbackStatus callback) {
|
||||
TriggerOnChange(ConsoleTriggerType::Motion);
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouch(Common::Input::CallbackStatus callback,
|
||||
[[maybe_unused]] std::size_t index) {
|
||||
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= console.touch_values.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -155,14 +155,14 @@ private:
|
||||
* Updates the motion status of the console
|
||||
* @param callback A CallbackStatus containing gyro and accelerometer data
|
||||
*/
|
||||
void SetMotion(Common::Input::CallbackStatus callback);
|
||||
void SetMotion(const Common::Input::CallbackStatus& callback);
|
||||
|
||||
/**
|
||||
* Updates the touch status of the console
|
||||
* @param callback A CallbackStatus containing the touch position
|
||||
* @param index Finger ID to be updated
|
||||
*/
|
||||
void SetTouch(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the console status
|
||||
|
||||
@@ -205,11 +205,12 @@ void EmulatedController::ReloadInput() {
|
||||
continue;
|
||||
}
|
||||
const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index, uuid](Common::Input::CallbackStatus callback) {
|
||||
SetButton(callback, index, uuid);
|
||||
}};
|
||||
button_devices[index]->SetCallback(button_callback);
|
||||
button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, uuid);
|
||||
},
|
||||
});
|
||||
button_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -218,11 +219,12 @@ void EmulatedController::ReloadInput() {
|
||||
continue;
|
||||
}
|
||||
const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
|
||||
Common::Input::InputCallback stick_callback{
|
||||
[this, index, uuid](Common::Input::CallbackStatus callback) {
|
||||
SetStick(callback, index, uuid);
|
||||
}};
|
||||
stick_devices[index]->SetCallback(stick_callback);
|
||||
stick_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, uuid);
|
||||
},
|
||||
});
|
||||
stick_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -231,11 +233,12 @@ void EmulatedController::ReloadInput() {
|
||||
continue;
|
||||
}
|
||||
const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
|
||||
Common::Input::InputCallback trigger_callback{
|
||||
[this, index, uuid](Common::Input::CallbackStatus callback) {
|
||||
SetTrigger(callback, index, uuid);
|
||||
}};
|
||||
trigger_devices[index]->SetCallback(trigger_callback);
|
||||
trigger_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetTrigger(callback, index, uuid);
|
||||
},
|
||||
});
|
||||
trigger_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -243,9 +246,12 @@ void EmulatedController::ReloadInput() {
|
||||
if (!battery_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback battery_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) { SetBattery(callback, index); }};
|
||||
battery_devices[index]->SetCallback(battery_callback);
|
||||
battery_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetBattery(callback, index);
|
||||
},
|
||||
});
|
||||
battery_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -253,9 +259,12 @@ void EmulatedController::ReloadInput() {
|
||||
if (!motion_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback motion_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) { SetMotion(callback, index); }};
|
||||
motion_devices[index]->SetCallback(motion_callback);
|
||||
motion_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetMotion(callback, index);
|
||||
},
|
||||
});
|
||||
motion_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -267,22 +276,24 @@ void EmulatedController::ReloadInput() {
|
||||
if (!tas_button_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index, tas_uuid](Common::Input::CallbackStatus callback) {
|
||||
SetButton(callback, index, tas_uuid);
|
||||
}};
|
||||
tas_button_devices[index]->SetCallback(button_callback);
|
||||
tas_button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, tas_uuid);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
|
||||
if (!tas_stick_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback stick_callback{
|
||||
[this, index, tas_uuid](Common::Input::CallbackStatus callback) {
|
||||
SetStick(callback, index, tas_uuid);
|
||||
}};
|
||||
tas_stick_devices[index]->SetCallback(stick_callback);
|
||||
tas_stick_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, tas_uuid);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +451,7 @@ void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage
|
||||
if (index >= button_params.size()) {
|
||||
return;
|
||||
}
|
||||
button_params[index] = param;
|
||||
button_params[index] = std::move(param);
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
@@ -448,7 +459,7 @@ void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage p
|
||||
if (index >= stick_params.size()) {
|
||||
return;
|
||||
}
|
||||
stick_params[index] = param;
|
||||
stick_params[index] = std::move(param);
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
@@ -456,11 +467,11 @@ void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage
|
||||
if (index >= motion_params.size()) {
|
||||
return;
|
||||
}
|
||||
motion_params[index] = param;
|
||||
motion_params[index] = std::move(param);
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
void EmulatedController::SetButton(Common::Input::CallbackStatus callback, std::size_t index,
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
if (index >= controller.button_values.size()) {
|
||||
return;
|
||||
@@ -600,7 +611,7 @@ void EmulatedController::SetButton(Common::Input::CallbackStatus callback, std::
|
||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetStick(Common::Input::CallbackStatus callback, std::size_t index,
|
||||
void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
if (index >= controller.stick_values.size()) {
|
||||
return;
|
||||
@@ -650,8 +661,8 @@ void EmulatedController::SetStick(Common::Input::CallbackStatus callback, std::s
|
||||
TriggerOnChange(ControllerTriggerType::Stick, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index, Common::UUID uuid) {
|
||||
if (index >= controller.trigger_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -659,7 +670,7 @@ void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std:
|
||||
const auto trigger_value = TransformToTrigger(callback);
|
||||
|
||||
// Only read trigger values that have the same uuid or are pressed once
|
||||
if (controller.stick_values[index].uuid != uuid) {
|
||||
if (controller.trigger_values[index].uuid != uuid) {
|
||||
if (!trigger_value.pressed.value) {
|
||||
return;
|
||||
}
|
||||
@@ -675,7 +686,7 @@ void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto trigger = controller.trigger_values[index];
|
||||
const auto& trigger = controller.trigger_values[index];
|
||||
|
||||
switch (index) {
|
||||
case Settings::NativeTrigger::LTrigger:
|
||||
@@ -692,7 +703,8 @@ void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std:
|
||||
TriggerOnChange(ControllerTriggerType::Trigger, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetMotion(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= controller.motion_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -730,7 +742,8 @@ void EmulatedController::SetMotion(Common::Input::CallbackStatus callback, std::
|
||||
TriggerOnChange(ControllerTriggerType::Motion, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetBattery(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= controller.battery_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -1110,7 +1123,7 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
|
||||
|
||||
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, update_callback);
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
|
||||
@@ -328,35 +328,38 @@ private:
|
||||
* @param callback A CallbackStatus containing the button status
|
||||
* @param index Button ID of the to be updated
|
||||
*/
|
||||
void SetButton(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
|
||||
void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid);
|
||||
|
||||
/**
|
||||
* Updates the analog stick status of the controller
|
||||
* @param callback A CallbackStatus containing the analog stick status
|
||||
* @param index stick ID of the to be updated
|
||||
*/
|
||||
void SetStick(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
|
||||
void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid);
|
||||
|
||||
/**
|
||||
* Updates the trigger status of the controller
|
||||
* @param callback A CallbackStatus containing the trigger status
|
||||
* @param index trigger ID of the to be updated
|
||||
*/
|
||||
void SetTrigger(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
|
||||
void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid);
|
||||
|
||||
/**
|
||||
* Updates the motion status of the controller
|
||||
* @param callback A CallbackStatus containing gyro and accelerometer data
|
||||
* @param index motion ID of the to be updated
|
||||
*/
|
||||
void SetMotion(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the battery status of the controller
|
||||
* @param callback A CallbackStatus containing the battery status
|
||||
* @param index Button ID of the to be updated
|
||||
*/
|
||||
void SetBattery(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the controller status
|
||||
|
||||
@@ -70,50 +70,55 @@ void EmulatedDevices::ReloadInput() {
|
||||
if (!mouse_button_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetMouseButton(callback, index);
|
||||
}};
|
||||
mouse_button_devices[index]->SetCallback(button_callback);
|
||||
mouse_button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetMouseButton(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
|
||||
if (!mouse_analog_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetMouseAnalog(callback, index);
|
||||
}};
|
||||
mouse_analog_devices[index]->SetCallback(button_callback);
|
||||
mouse_analog_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetMouseAnalog(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (mouse_stick_device) {
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this](Common::Input::CallbackStatus callback) { SetMouseStick(callback); }};
|
||||
mouse_stick_device->SetCallback(button_callback);
|
||||
mouse_stick_device->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); },
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
|
||||
if (!keyboard_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetKeyboardButton(callback, index);
|
||||
}};
|
||||
keyboard_devices[index]->SetCallback(button_callback);
|
||||
keyboard_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetKeyboardButton(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
|
||||
if (!keyboard_modifier_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetKeyboardModifier(callback, index);
|
||||
}};
|
||||
keyboard_modifier_devices[index]->SetCallback(button_callback);
|
||||
keyboard_modifier_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetKeyboardModifier(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +164,8 @@ void EmulatedDevices::RestoreConfig() {
|
||||
ReloadFromSettings();
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.keyboard_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -216,7 +222,7 @@ void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetKeyboardModifier(Common::Input::CallbackStatus callback,
|
||||
void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.keyboard_moddifier_values.size()) {
|
||||
return;
|
||||
@@ -286,7 +292,8 @@ void EmulatedDevices::SetKeyboardModifier(Common::Input::CallbackStatus callback
|
||||
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.mouse_button_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -347,7 +354,8 @@ void EmulatedDevices::SetMouseButton(Common::Input::CallbackStatus callback, std
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.mouse_analog_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -374,7 +382,7 @@ void EmulatedDevices::SetMouseAnalog(Common::Input::CallbackStatus callback, std
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseStick(Common::Input::CallbackStatus callback) {
|
||||
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
const auto touch_value = TransformToTouch(callback);
|
||||
|
||||
@@ -435,7 +443,7 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
|
||||
|
||||
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, update_callback);
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,35 +156,34 @@ private:
|
||||
* @param callback A CallbackStatus containing the key status
|
||||
* @param index key ID to be updated
|
||||
*/
|
||||
void SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the keyboard status of the keyboard device
|
||||
* @param callback A CallbackStatus containing the modifier key status
|
||||
* @param index modifier key ID to be updated
|
||||
*/
|
||||
void SetKeyboardModifier(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the mouse button status of the mouse device
|
||||
* @param callback A CallbackStatus containing the button status
|
||||
* @param index Button ID to be updated
|
||||
*/
|
||||
void SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the mouse wheel status of the mouse device
|
||||
* @param callback A CallbackStatus containing the wheel status
|
||||
* @param index wheel ID to be updated
|
||||
*/
|
||||
void SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the mouse position status of the mouse device
|
||||
* @param callback A CallbackStatus containing the position status
|
||||
* @param index stick ID to be updated
|
||||
*/
|
||||
void SetMouseStick(Common::Input::CallbackStatus callback);
|
||||
void SetMouseStick(const Common::Input::CallbackStatus& callback);
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the device status
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
@@ -28,7 +29,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
|
||||
|
||||
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
@@ -58,7 +59,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
|
||||
|
||||
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
@@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
|
||||
return true;
|
||||
}
|
||||
|
||||
class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
|
||||
public:
|
||||
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
|
||||
: KThreadQueue(kernel_), m_tree(t) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// If the thread is waiting on an address arbiter, remove it from the tree.
|
||||
if (waiting_thread->IsWaitingForAddressArbiter()) {
|
||||
m_tree->erase(m_tree->iterator_to(*waiting_thread));
|
||||
waiting_thread->ClearAddressArbiter();
|
||||
}
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
|
||||
private:
|
||||
KAddressArbiter::ThreadTree* m_tree;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
|
||||
@@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
|
||||
auto it = thread_tree.nfind_light({addr, -1});
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
// End the thread's wait.
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
target_thread->EndWait(ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->Wakeup();
|
||||
target_thread->ClearAddressArbiter();
|
||||
|
||||
it = thread_tree.erase(it);
|
||||
target_thread->ClearAddressArbiter();
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
@@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
|
||||
auto it = thread_tree.nfind_light({addr, -1});
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
// End the thread's wait.
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
target_thread->EndWait(ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->Wakeup();
|
||||
target_thread->ClearAddressArbiter();
|
||||
|
||||
it = thread_tree.erase(it);
|
||||
target_thread->ClearAddressArbiter();
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
@@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
// End the thread's wait.
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
target_thread->EndWait(ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->Wakeup();
|
||||
target_thread->ClearAddressArbiter();
|
||||
|
||||
it = thread_tree.erase(it);
|
||||
target_thread->ClearAddressArbiter();
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
@@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
|
||||
@@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
|
||||
return ResultTerminationRequested;
|
||||
}
|
||||
|
||||
// Set the synced object.
|
||||
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
|
||||
|
||||
// Read the value from userspace.
|
||||
s32 user_value{};
|
||||
bool succeeded{};
|
||||
@@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
|
||||
// Set the arbiter.
|
||||
cur_thread->SetAddressArbiter(&thread_tree, addr);
|
||||
thread_tree.insert(*cur_thread);
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
|
||||
// Wait for the thread to finish.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
|
||||
}
|
||||
|
||||
// Cancel the timer wait.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
|
||||
|
||||
// Remove from the address arbiter.
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
if (cur_thread->IsWaitingForAddressArbiter()) {
|
||||
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
|
||||
cur_thread->ClearAddressArbiter();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the result.
|
||||
KSynchronizationObject* dummy{};
|
||||
return cur_thread->GetWaitResult(&dummy);
|
||||
return cur_thread->GetWaitResult();
|
||||
}
|
||||
|
||||
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
|
||||
@@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
||||
return ResultTerminationRequested;
|
||||
}
|
||||
|
||||
// Set the synced object.
|
||||
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
|
||||
|
||||
// Read the value from userspace.
|
||||
s32 user_value{};
|
||||
if (!ReadFromUser(system, &user_value, addr)) {
|
||||
@@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
||||
// Set the arbiter.
|
||||
cur_thread->SetAddressArbiter(&thread_tree, addr);
|
||||
thread_tree.insert(*cur_thread);
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
|
||||
// Wait for the thread to finish.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
|
||||
}
|
||||
|
||||
// Cancel the timer wait.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
|
||||
|
||||
// Remove from the address arbiter.
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
if (cur_thread->IsWaitingForAddressArbiter()) {
|
||||
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
|
||||
cur_thread->ClearAddressArbiter();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the result.
|
||||
KSynchronizationObject* dummy{};
|
||||
return cur_thread->GetWaitResult(&dummy);
|
||||
return cur_thread->GetWaitResult();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -170,6 +170,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterWithKernel();
|
||||
void UnregisterWithKernel();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
@@ -33,7 +34,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
|
||||
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
|
||||
u32 new_orr_mask) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// Load the value from the address.
|
||||
const auto expected = monitor.ExclusiveRead32(current_core, address);
|
||||
@@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
|
||||
return true;
|
||||
}
|
||||
|
||||
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
|
||||
public:
|
||||
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
|
||||
: KThreadQueue(kernel_) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Remove the thread as a waiter from its owner.
|
||||
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
|
||||
private:
|
||||
KConditionVariable::ThreadTree* m_tree;
|
||||
|
||||
public:
|
||||
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
|
||||
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
|
||||
: KThreadQueue(kernel_), m_tree(t) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Remove the thread as a waiter from its owner.
|
||||
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(waiting_thread);
|
||||
}
|
||||
|
||||
// If the thread is waiting on a condvar, remove it from the tree.
|
||||
if (waiting_thread->IsWaitingForConditionVariable()) {
|
||||
m_tree->erase(m_tree->iterator_to(*waiting_thread));
|
||||
waiting_thread->ClearConditionVariable();
|
||||
}
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
KConditionVariable::KConditionVariable(Core::System& system_)
|
||||
@@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||
|
||||
// Determine the next tag.
|
||||
u32 next_value{};
|
||||
if (next_owner_thread) {
|
||||
if (next_owner_thread != nullptr) {
|
||||
next_value = next_owner_thread->GetAddressKeyValue();
|
||||
if (num_waiters > 1) {
|
||||
next_value |= Svc::HandleWaitMask;
|
||||
}
|
||||
|
||||
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
next_owner_thread->Wakeup();
|
||||
}
|
||||
|
||||
// Write the value to userspace.
|
||||
if (!WriteToUser(system, addr, std::addressof(next_value))) {
|
||||
if (next_owner_thread) {
|
||||
next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
|
||||
// Write the value to userspace.
|
||||
ResultCode result{ResultSuccess};
|
||||
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
|
||||
result = ResultSuccess;
|
||||
} else {
|
||||
result = ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
return ResultInvalidCurrentMemory;
|
||||
// Signal the next owner thread.
|
||||
next_owner_thread->EndWait(result);
|
||||
return result;
|
||||
} else {
|
||||
// Just write the value to userspace.
|
||||
R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
|
||||
|
||||
// Wait for the address.
|
||||
{
|
||||
KScopedAutoObject<KThread> owner_thread;
|
||||
ASSERT(owner_thread.IsNull());
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
cur_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
|
||||
// Check if the thread should terminate.
|
||||
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
|
||||
|
||||
{
|
||||
// Read the tag from userspace.
|
||||
u32 test_tag{};
|
||||
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// If the tag isn't the handle (with wait mask), we're done.
|
||||
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
|
||||
|
||||
// Get the lock owner thread.
|
||||
owner_thread =
|
||||
kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
|
||||
handle);
|
||||
R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Update the lock.
|
||||
cur_thread->SetAddressKey(addr, value);
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
|
||||
cur_thread->SetMutexWaitAddressForDebugging(addr);
|
||||
}
|
||||
}
|
||||
ASSERT(owner_thread.IsNotNull());
|
||||
}
|
||||
|
||||
// Remove the thread as a waiter from the lock owner.
|
||||
KThread* owner_thread{};
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
KThread* owner_thread = cur_thread->GetLockOwner();
|
||||
if (owner_thread != nullptr) {
|
||||
owner_thread->RemoveWaiter(cur_thread);
|
||||
}
|
||||
|
||||
// Check if the thread should terminate.
|
||||
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
|
||||
|
||||
// Read the tag from userspace.
|
||||
u32 test_tag{};
|
||||
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
|
||||
|
||||
// If the tag isn't the handle (with wait mask), we're done.
|
||||
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
|
||||
|
||||
// Get the lock owner thread.
|
||||
owner_thread = kernel.CurrentProcess()
|
||||
->GetHandleTable()
|
||||
.GetObjectWithoutPseudoHandle<KThread>(handle)
|
||||
.ReleasePointerUnsafe();
|
||||
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
|
||||
|
||||
// Update the lock.
|
||||
cur_thread->SetAddressKey(addr, value);
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
|
||||
// Begin waiting.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
|
||||
cur_thread->SetMutexWaitAddressForDebugging(addr);
|
||||
}
|
||||
|
||||
// Close our reference to the owner thread, now that the wait is over.
|
||||
owner_thread->Close();
|
||||
|
||||
// Get the wait result.
|
||||
KSynchronizationObject* dummy{};
|
||||
return cur_thread->GetWaitResult(std::addressof(dummy));
|
||||
return cur_thread->GetWaitResult();
|
||||
}
|
||||
|
||||
KThread* KConditionVariable::SignalImpl(KThread* thread) {
|
||||
void KConditionVariable::SignalImpl(KThread* thread) {
|
||||
// Check pre-conditions.
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
@@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
can_access = true;
|
||||
if (can_access) {
|
||||
if (can_access) [[likely]] {
|
||||
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
|
||||
Svc::HandleWaitMask);
|
||||
}
|
||||
}
|
||||
|
||||
KThread* thread_to_close = nullptr;
|
||||
if (can_access) {
|
||||
if (can_access) [[likely]] {
|
||||
if (prev_tag == Svc::InvalidHandle) {
|
||||
// If nobody held the lock previously, we're all good.
|
||||
thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
thread->Wakeup();
|
||||
thread->EndWait(ResultSuccess);
|
||||
} else {
|
||||
// Get the previous owner.
|
||||
KThread* owner_thread = kernel.CurrentProcess()
|
||||
@@ -189,33 +223,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
|
||||
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
|
||||
.ReleasePointerUnsafe();
|
||||
|
||||
if (owner_thread) {
|
||||
if (owner_thread) [[likely]] {
|
||||
// Add the thread as a waiter on the owner.
|
||||
owner_thread->AddWaiter(thread);
|
||||
thread_to_close = owner_thread;
|
||||
owner_thread->Close();
|
||||
} else {
|
||||
// The lock was tagged with a thread that doesn't exist.
|
||||
thread->SetSyncedObject(nullptr, ResultInvalidState);
|
||||
thread->Wakeup();
|
||||
thread->EndWait(ResultInvalidState);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the address wasn't accessible, note so.
|
||||
thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
|
||||
thread->Wakeup();
|
||||
thread->EndWait(ResultInvalidCurrentMemory);
|
||||
}
|
||||
|
||||
return thread_to_close;
|
||||
}
|
||||
|
||||
void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
// Prepare for signaling.
|
||||
constexpr int MaxThreads = 16;
|
||||
|
||||
KLinkedList<KThread> thread_list{kernel};
|
||||
std::array<KThread*, MaxThreads> thread_array;
|
||||
s32 num_to_close{};
|
||||
|
||||
// Perform signaling.
|
||||
s32 num_waiters{};
|
||||
{
|
||||
@@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
(it->GetConditionVariableKey() == cv_key)) {
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
|
||||
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
|
||||
if (num_to_close < MaxThreads) {
|
||||
thread_array[num_to_close++] = thread;
|
||||
} else {
|
||||
thread_list.push_back(*thread);
|
||||
}
|
||||
}
|
||||
|
||||
this->SignalImpl(target_thread);
|
||||
it = thread_tree.erase(it);
|
||||
target_thread->ClearConditionVariable();
|
||||
++num_waiters;
|
||||
@@ -245,27 +261,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
|
||||
}
|
||||
}
|
||||
|
||||
// Close threads in the array.
|
||||
for (auto i = 0; i < num_to_close; ++i) {
|
||||
thread_array[i]->Close();
|
||||
}
|
||||
|
||||
// Close threads in the list.
|
||||
for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
|
||||
(*it).Close();
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
|
||||
kernel, std::addressof(thread_tree));
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
|
||||
|
||||
// Set the synced object.
|
||||
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
|
||||
KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
|
||||
|
||||
// Check that the thread isn't terminating.
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
@@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
|
||||
}
|
||||
|
||||
// Wake up the next owner.
|
||||
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
next_owner_thread->Wakeup();
|
||||
next_owner_thread->EndWait(ResultSuccess);
|
||||
}
|
||||
|
||||
// Write to the cv key.
|
||||
@@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// If timeout is zero, time out.
|
||||
R_UNLESS(timeout != 0, ResultTimedOut);
|
||||
|
||||
// Update condition variable tracking.
|
||||
{
|
||||
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
|
||||
thread_tree.insert(*cur_thread);
|
||||
}
|
||||
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
|
||||
thread_tree.insert(*cur_thread);
|
||||
|
||||
// If the timeout is non-zero, set the thread as waiting.
|
||||
if (timeout != 0) {
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
|
||||
cur_thread->SetMutexWaitAddressForDebugging(addr);
|
||||
}
|
||||
// Begin waiting.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
|
||||
cur_thread->SetMutexWaitAddressForDebugging(addr);
|
||||
}
|
||||
|
||||
// Cancel the timer wait.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
|
||||
|
||||
// Remove from the condition variable.
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(cur_thread);
|
||||
}
|
||||
|
||||
if (cur_thread->IsWaitingForConditionVariable()) {
|
||||
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
|
||||
cur_thread->ClearConditionVariable();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the result.
|
||||
KSynchronizationObject* dummy{};
|
||||
return cur_thread->GetWaitResult(std::addressof(dummy));
|
||||
// Get the wait result.
|
||||
return cur_thread->GetWaitResult();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
|
||||
|
||||
private:
|
||||
[[nodiscard]] KThread* SignalImpl(KThread* thread);
|
||||
void SignalImpl(KThread* thread);
|
||||
|
||||
ThreadTree thread_tree;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
|
||||
// Get the table and clear our record of it.
|
||||
u16 saved_table_size = 0;
|
||||
{
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
std::swap(m_table_size, saved_table_size);
|
||||
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
// Find the object and free the entry.
|
||||
KAutoObject* obj = nullptr;
|
||||
{
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if (this->IsValidHandle(handle)) {
|
||||
@@ -62,6 +64,7 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
}
|
||||
|
||||
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -84,6 +87,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
|
||||
}
|
||||
|
||||
ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -94,6 +98,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Unreserve(Handle handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -112,6 +117,7 @@ void KHandleTable::Unreserve(Handle handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
|
||||
@@ -68,6 +68,7 @@ public:
|
||||
template <typename T = KAutoObject>
|
||||
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
|
||||
// Lock and look up in table.
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
@@ -122,6 +123,7 @@ public:
|
||||
size_t num_opened;
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
for (num_opened = 0; num_opened < num_handles; num_opened++) {
|
||||
// Get the current handle.
|
||||
|
||||
80
src/core/hle/kernel/k_light_condition_variable.cpp
Normal file
80
src/core/hle/kernel/k_light_condition_variable.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_light_condition_variable.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
|
||||
public:
|
||||
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
|
||||
bool term)
|
||||
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Only process waits if we're allowed to.
|
||||
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the thread from the waiting thread from the light condition variable.
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
|
||||
private:
|
||||
KThread::WaiterList* m_wait_list;
|
||||
bool m_allow_terminating_thread;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
|
||||
// Create thread queue.
|
||||
KThread* owner = GetCurrentThreadPointer(kernel);
|
||||
|
||||
ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
|
||||
allow_terminating_thread);
|
||||
|
||||
// Sleep the thread.
|
||||
{
|
||||
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
|
||||
|
||||
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
|
||||
lk.CancelSleep();
|
||||
return;
|
||||
}
|
||||
|
||||
lock->Unlock();
|
||||
|
||||
// Add the thread to the queue.
|
||||
wait_list.push_back(*owner);
|
||||
|
||||
// Begin waiting.
|
||||
owner->BeginWait(std::addressof(wait_queue));
|
||||
}
|
||||
|
||||
// Re-acquire the lock.
|
||||
lock->Lock();
|
||||
}
|
||||
|
||||
void KLightConditionVariable::Broadcast() {
|
||||
KScopedSchedulerLock lk(kernel);
|
||||
|
||||
// Signal all threads.
|
||||
for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
|
||||
it->EndWait(ResultSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -2,72 +2,24 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class KLightLock;
|
||||
|
||||
class KLightConditionVariable {
|
||||
public:
|
||||
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
|
||||
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
|
||||
WaitImpl(lock, timeout, allow_terminating_thread);
|
||||
}
|
||||
|
||||
void Broadcast() {
|
||||
KScopedSchedulerLock lk{kernel};
|
||||
|
||||
// Signal all threads.
|
||||
for (auto& thread : wait_list) {
|
||||
thread.SetState(ThreadState::Runnable);
|
||||
}
|
||||
}
|
||||
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
|
||||
void Broadcast();
|
||||
|
||||
private:
|
||||
void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
|
||||
KThread* owner = GetCurrentThreadPointer(kernel);
|
||||
|
||||
// Sleep the thread.
|
||||
{
|
||||
KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
|
||||
|
||||
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
|
||||
lk.CancelSleep();
|
||||
return;
|
||||
}
|
||||
|
||||
lock->Unlock();
|
||||
|
||||
// Set the thread as waiting.
|
||||
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
|
||||
|
||||
// Add the thread to the queue.
|
||||
wait_list.push_back(GetCurrentThread(kernel));
|
||||
}
|
||||
|
||||
// Remove the thread from the wait list.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
|
||||
}
|
||||
|
||||
// Cancel the task that the sleep setup.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(owner);
|
||||
|
||||
// Re-acquire the lock.
|
||||
lock->Lock();
|
||||
}
|
||||
|
||||
KernelCore& kernel;
|
||||
KThread::WaiterList wait_list{};
|
||||
};
|
||||
|
||||
@@ -5,44 +5,59 @@
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
class ThreadQueueImplForKLightLock final : public KThreadQueue {
|
||||
public:
|
||||
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Remove the thread as a waiter from its owner.
|
||||
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(waiting_thread);
|
||||
}
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void KLightLock::Lock() {
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
|
||||
const uintptr_t cur_thread_tag = (cur_thread | 1);
|
||||
|
||||
while (true) {
|
||||
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
|
||||
|
||||
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
|
||||
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
|
||||
std::memory_order_acquire)) {
|
||||
if ((old_tag | 1) == cur_thread_tag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
|
||||
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
|
||||
break;
|
||||
}
|
||||
|
||||
LockSlowPath(old_tag | 1, cur_thread);
|
||||
}
|
||||
}
|
||||
|
||||
void KLightLock::Unlock() {
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
|
||||
|
||||
uintptr_t expected = cur_thread;
|
||||
do {
|
||||
if (expected != cur_thread) {
|
||||
return UnlockSlowPath(cur_thread);
|
||||
}
|
||||
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
|
||||
if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
|
||||
this->UnlockSlowPath(cur_thread);
|
||||
}
|
||||
}
|
||||
|
||||
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
|
||||
ThreadQueueImplForKLightLock wait_queue(kernel);
|
||||
|
||||
// Pend the current thread waiting on the owner thread.
|
||||
{
|
||||
@@ -50,7 +65,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
|
||||
// Ensure we actually have locking to do.
|
||||
if (tag.load(std::memory_order_relaxed) != _owner) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the current thread as a waiter on the owner.
|
||||
@@ -58,22 +73,15 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
|
||||
// Set thread states.
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
// Begin waiting to hold the lock.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
|
||||
if (owner_thread->IsSuspended()) {
|
||||
owner_thread->ContinueIfHasKernelWaiters();
|
||||
}
|
||||
}
|
||||
|
||||
// We're no longer waiting on the lock owner.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
|
||||
owner_thread->RemoveWaiter(cur_thread);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
@@ -81,22 +89,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
|
||||
// Unlock.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Get the next owner.
|
||||
s32 num_waiters = 0;
|
||||
s32 num_waiters;
|
||||
KThread* next_owner = owner_thread->RemoveWaiterByKey(
|
||||
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
|
||||
|
||||
// Pass the lock to the next owner.
|
||||
uintptr_t next_tag = 0;
|
||||
if (next_owner != nullptr) {
|
||||
next_tag = reinterpret_cast<uintptr_t>(next_owner);
|
||||
if (num_waiters > 1) {
|
||||
next_tag |= 0x1;
|
||||
}
|
||||
next_tag =
|
||||
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
|
||||
|
||||
next_owner->SetState(ThreadState::Runnable);
|
||||
next_owner->EndWait(ResultSuccess);
|
||||
|
||||
if (next_owner->IsSuspended()) {
|
||||
next_owner->ContinueIfHasKernelWaiters();
|
||||
@@ -110,7 +116,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
}
|
||||
|
||||
// Write the new tag value.
|
||||
tag.store(next_tag);
|
||||
tag.store(next_tag, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
|
||||
void Unlock();
|
||||
|
||||
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
|
||||
bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
|
||||
|
||||
void UnlockSlowPath(uintptr_t cur_thread);
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
|
||||
thread->GetContext64().cpu_registers[0] = 0;
|
||||
thread->GetContext32().cpu_registers[1] = thread_handle;
|
||||
thread->GetContext64().cpu_registers[1] = thread_handle;
|
||||
thread->DisableDispatch();
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
@@ -227,12 +228,15 @@ void KProcess::PinCurrentThread() {
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
// If the thread isn't terminated, pin it.
|
||||
if (!cur_thread->IsTerminationRequested()) {
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::UnpinCurrentThread() {
|
||||
@@ -250,6 +254,20 @@ void KProcess::UnpinCurrentThread() {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
|
||||
void KProcess::UnpinThread(KThread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the thread's core id.
|
||||
const auto core_id = thread->GetActiveCore();
|
||||
|
||||
// Unpin it.
|
||||
UnpinThread(core_id, thread);
|
||||
thread->Unpin();
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
|
||||
ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
|
||||
[[maybe_unused]] size_t size) {
|
||||
// Lock ourselves, to prevent concurrent access.
|
||||
|
||||
@@ -347,6 +347,7 @@ public:
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
void UnpinThread(KThread* thread);
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
return state_lock;
|
||||
|
||||
@@ -240,8 +240,8 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
|
||||
|
||||
// If the thread is runnable, we want to change its priority in the queue.
|
||||
if (thread->GetRawState() == ThreadState::Runnable) {
|
||||
GetPriorityQueue(kernel).ChangePriority(
|
||||
old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread);
|
||||
GetPriorityQueue(kernel).ChangePriority(old_priority,
|
||||
thread == kernel.GetCurrentEmuThread(), thread);
|
||||
IncrementScheduledCount(thread);
|
||||
SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
@@ -360,7 +360,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
|
||||
}
|
||||
|
||||
bool KScheduler::CanSchedule(KernelCore& kernel) {
|
||||
return kernel.CurrentScheduler()->GetCurrentThread()->GetDisableDispatchCount() <= 1;
|
||||
return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
|
||||
}
|
||||
|
||||
bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
|
||||
@@ -376,20 +376,30 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
|
||||
}
|
||||
|
||||
void KScheduler::DisableScheduling(KernelCore& kernel) {
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
|
||||
scheduler->GetCurrentThread()->DisableDispatch();
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (kernel.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
|
||||
GetCurrentThreadPointer(kernel)->DisableDispatch();
|
||||
}
|
||||
|
||||
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
|
||||
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
|
||||
scheduler->GetCurrentThread()->EnableDispatch();
|
||||
}
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (kernel.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* current_thread = GetCurrentThreadPointer(kernel);
|
||||
|
||||
ASSERT(current_thread->GetDisableDispatchCount() >= 1);
|
||||
|
||||
if (current_thread->GetDisableDispatchCount() > 1) {
|
||||
current_thread->EnableDispatch();
|
||||
} else {
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
|
||||
@@ -617,13 +627,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
|
||||
state.highest_priority_thread = nullptr;
|
||||
}
|
||||
|
||||
KScheduler::~KScheduler() {
|
||||
void KScheduler::Finalize() {
|
||||
if (idle_thread) {
|
||||
idle_thread->Close();
|
||||
idle_thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
KScheduler::~KScheduler() {
|
||||
ASSERT(!idle_thread);
|
||||
}
|
||||
|
||||
KThread* KScheduler::GetCurrentThread() const {
|
||||
if (auto result = current_thread.load(); result) {
|
||||
return result;
|
||||
@@ -642,10 +656,12 @@ void KScheduler::RescheduleCurrentCore() {
|
||||
if (phys_core.IsInterrupted()) {
|
||||
phys_core.ClearInterrupt();
|
||||
}
|
||||
|
||||
guard.Lock();
|
||||
if (state.needs_scheduling.load()) {
|
||||
Schedule();
|
||||
} else {
|
||||
GetCurrentThread()->EnableDispatch();
|
||||
guard.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -655,26 +671,33 @@ void KScheduler::OnThreadStart() {
|
||||
}
|
||||
|
||||
void KScheduler::Unload(KThread* thread) {
|
||||
ASSERT(thread);
|
||||
|
||||
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
|
||||
if (thread) {
|
||||
if (thread->IsCallingSvc()) {
|
||||
thread->ClearIsCallingSvc();
|
||||
}
|
||||
if (!thread->IsTerminationRequested()) {
|
||||
prev_thread = thread;
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.SaveContext(thread->GetContext32());
|
||||
cpu_core.SaveContext(thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
} else {
|
||||
prev_thread = nullptr;
|
||||
}
|
||||
thread->context_guard.Unlock();
|
||||
if (thread->IsCallingSvc()) {
|
||||
thread->ClearIsCallingSvc();
|
||||
}
|
||||
|
||||
auto& physical_core = system.Kernel().PhysicalCore(core_id);
|
||||
if (!physical_core.IsInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
|
||||
cpu_core.SaveContext(thread->GetContext32());
|
||||
cpu_core.SaveContext(thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
|
||||
if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
|
||||
prev_thread = thread;
|
||||
} else {
|
||||
prev_thread = nullptr;
|
||||
}
|
||||
|
||||
thread->context_guard.Unlock();
|
||||
}
|
||||
|
||||
void KScheduler::Reload(KThread* thread) {
|
||||
@@ -683,11 +706,6 @@ void KScheduler::Reload(KThread* thread) {
|
||||
if (thread) {
|
||||
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
|
||||
|
||||
auto* const thread_owner_process = thread->GetOwnerProcess();
|
||||
if (thread_owner_process != nullptr) {
|
||||
system.Kernel().MakeCurrentProcess(thread_owner_process);
|
||||
}
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
@@ -705,7 +723,7 @@ void KScheduler::SwitchContextStep2() {
|
||||
}
|
||||
|
||||
void KScheduler::ScheduleImpl() {
|
||||
KThread* previous_thread = current_thread.load();
|
||||
KThread* previous_thread = GetCurrentThread();
|
||||
KThread* next_thread = state.highest_priority_thread;
|
||||
|
||||
state.needs_scheduling = false;
|
||||
@@ -717,10 +735,15 @@ void KScheduler::ScheduleImpl() {
|
||||
|
||||
// If we're not actually switching thread, there's nothing to do.
|
||||
if (next_thread == current_thread.load()) {
|
||||
previous_thread->EnableDispatch();
|
||||
guard.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != core_id) {
|
||||
next_thread->SetCurrentCore(core_id);
|
||||
}
|
||||
|
||||
current_thread.store(next_thread);
|
||||
|
||||
KProcess* const previous_process = system.Kernel().CurrentProcess();
|
||||
@@ -731,11 +754,7 @@ void KScheduler::ScheduleImpl() {
|
||||
Unload(previous_thread);
|
||||
|
||||
std::shared_ptr<Common::Fiber>* old_context;
|
||||
if (previous_thread != nullptr) {
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
} else {
|
||||
old_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
guard.Unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
explicit KScheduler(Core::System& system_, s32 core_id_);
|
||||
~KScheduler();
|
||||
|
||||
void Finalize();
|
||||
|
||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||
void RescheduleCurrentCore();
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ public:
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (kernel.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsLockedByCurrentThread()) {
|
||||
// If we already own the lock, we can just increment the count.
|
||||
ASSERT(lock_count > 0);
|
||||
@@ -43,6 +48,11 @@ public:
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (kernel.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(IsLockedByCurrentThread());
|
||||
ASSERT(lock_count > 0);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
@@ -175,8 +175,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
if (!context.IsThreadWaiting()) {
|
||||
context.GetThread().Wakeup();
|
||||
context.GetThread().SetSyncedObject(nullptr, result);
|
||||
context.GetThread().EndWait(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,66 @@
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
|
||||
public:
|
||||
ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
|
||||
KSynchronizationObject::ThreadListNode* n, s32 c)
|
||||
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
|
||||
|
||||
void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
|
||||
ResultCode wait_result) override {
|
||||
// Determine the sync index, and unlink all nodes.
|
||||
s32 sync_index = -1;
|
||||
for (auto i = 0; i < m_count; ++i) {
|
||||
// Check if this is the signaled object.
|
||||
if (m_objects[i] == signaled_object && sync_index == -1) {
|
||||
sync_index = i;
|
||||
}
|
||||
|
||||
// Unlink the current node from the current object.
|
||||
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
|
||||
}
|
||||
|
||||
// Set the waiting thread's sync index.
|
||||
waiting_thread->SetSyncedIndex(sync_index);
|
||||
|
||||
// Set the waiting thread as not cancellable.
|
||||
waiting_thread->ClearCancellable();
|
||||
|
||||
// Invoke the base end wait handler.
|
||||
KThreadQueue::EndWait(waiting_thread, wait_result);
|
||||
}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Remove all nodes from our list.
|
||||
for (auto i = 0; i < m_count; ++i) {
|
||||
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
|
||||
}
|
||||
|
||||
// Set the waiting thread as not cancellable.
|
||||
waiting_thread->ClearCancellable();
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
|
||||
private:
|
||||
KSynchronizationObject** m_objects;
|
||||
KSynchronizationObject::ThreadListNode* m_nodes;
|
||||
s32 m_count;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void KSynchronizationObject::Finalize() {
|
||||
this->OnFinalizeSynchronizationObject();
|
||||
KAutoObject::Finalize();
|
||||
@@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
|
||||
std::vector<ThreadListNode> thread_nodes(num_objects);
|
||||
|
||||
// Prepare for wait.
|
||||
KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread();
|
||||
KThread* thread = GetCurrentThreadPointer(kernel_ctx);
|
||||
ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
|
||||
thread_nodes.data(), num_objects);
|
||||
|
||||
{
|
||||
// Setup the scheduling lock and sleep.
|
||||
KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout};
|
||||
KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
|
||||
|
||||
// Check if the thread should terminate.
|
||||
if (thread->IsTerminationRequested()) {
|
||||
slp.CancelSleep();
|
||||
return ResultTerminationRequested;
|
||||
}
|
||||
|
||||
// Check if any of the objects are already signaled.
|
||||
for (auto i = 0; i < num_objects; ++i) {
|
||||
@@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
|
||||
return ResultTimedOut;
|
||||
}
|
||||
|
||||
// Check if the thread should terminate.
|
||||
if (thread->IsTerminationRequested()) {
|
||||
slp.CancelSleep();
|
||||
return ResultTerminationRequested;
|
||||
}
|
||||
|
||||
// Check if waiting was canceled.
|
||||
if (thread->IsWaitCancelled()) {
|
||||
slp.CancelSleep();
|
||||
@@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
|
||||
thread_nodes[i].thread = thread;
|
||||
thread_nodes[i].next = nullptr;
|
||||
|
||||
if (objects[i]->thread_list_tail == nullptr) {
|
||||
objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
|
||||
} else {
|
||||
objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
|
||||
}
|
||||
|
||||
objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
|
||||
objects[i]->LinkNode(std::addressof(thread_nodes[i]));
|
||||
}
|
||||
|
||||
// For debugging only
|
||||
thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
|
||||
|
||||
// Mark the thread as waiting.
|
||||
// Mark the thread as cancellable.
|
||||
thread->SetCancellable();
|
||||
thread->SetSyncedObject(nullptr, ResultTimedOut);
|
||||
thread->SetState(ThreadState::Waiting);
|
||||
|
||||
// Clear the thread's synced index.
|
||||
thread->SetSyncedIndex(-1);
|
||||
|
||||
// Wait for an object to be signaled.
|
||||
thread->BeginWait(std::addressof(wait_queue));
|
||||
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
|
||||
}
|
||||
|
||||
// The lock/sleep is done, so we should be able to get our result.
|
||||
|
||||
// Thread is no longer cancellable.
|
||||
thread->ClearCancellable();
|
||||
|
||||
// For debugging only
|
||||
thread->SetWaitObjectsForDebugging({});
|
||||
|
||||
// Cancel the timer as needed.
|
||||
kernel_ctx.TimeManager().UnscheduleTimeEvent(thread);
|
||||
// Set the output index.
|
||||
*out_index = thread->GetSyncedIndex();
|
||||
|
||||
// Get the wait result.
|
||||
ResultCode wait_result{ResultSuccess};
|
||||
s32 sync_index = -1;
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel_ctx);
|
||||
KSynchronizationObject* synced_obj;
|
||||
wait_result = thread->GetWaitResult(std::addressof(synced_obj));
|
||||
|
||||
for (auto i = 0; i < num_objects; ++i) {
|
||||
// Unlink the object from the list.
|
||||
ThreadListNode* prev_ptr =
|
||||
reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
|
||||
ThreadListNode* prev_val = nullptr;
|
||||
ThreadListNode *prev, *tail_prev;
|
||||
|
||||
do {
|
||||
prev = prev_ptr;
|
||||
prev_ptr = prev_ptr->next;
|
||||
tail_prev = prev_val;
|
||||
prev_val = prev_ptr;
|
||||
} while (prev_ptr != std::addressof(thread_nodes[i]));
|
||||
|
||||
if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
|
||||
objects[i]->thread_list_tail = tail_prev;
|
||||
}
|
||||
|
||||
prev->next = thread_nodes[i].next;
|
||||
|
||||
if (objects[i] == synced_obj) {
|
||||
sync_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set output.
|
||||
*out_index = sync_index;
|
||||
return wait_result;
|
||||
return thread->GetWaitResult();
|
||||
}
|
||||
|
||||
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
|
||||
@@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
|
||||
KSynchronizationObject::~KSynchronizationObject() = default;
|
||||
|
||||
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// If we're not signaled, we've nothing to notify.
|
||||
if (!this->IsSignaled()) {
|
||||
@@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
|
||||
|
||||
// Iterate over each thread.
|
||||
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
|
||||
KThread* thread = cur_node->thread;
|
||||
if (thread->GetState() == ThreadState::Waiting) {
|
||||
thread->SetSyncedObject(this, result);
|
||||
thread->SetState(ThreadState::Runnable);
|
||||
}
|
||||
cur_node->thread->NotifyAvailable(this, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,38 @@ public:
|
||||
|
||||
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
|
||||
|
||||
void LinkNode(ThreadListNode* node_) {
|
||||
// Link the node to the list.
|
||||
if (thread_list_tail == nullptr) {
|
||||
thread_list_head = node_;
|
||||
} else {
|
||||
thread_list_tail->next = node_;
|
||||
}
|
||||
|
||||
thread_list_tail = node_;
|
||||
}
|
||||
|
||||
void UnlinkNode(ThreadListNode* node_) {
|
||||
// Unlink the node from the list.
|
||||
ThreadListNode* prev_ptr =
|
||||
reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
|
||||
ThreadListNode* prev_val = nullptr;
|
||||
ThreadListNode *prev, *tail_prev;
|
||||
|
||||
do {
|
||||
prev = prev_ptr;
|
||||
prev_ptr = prev_ptr->next;
|
||||
tail_prev = prev_val;
|
||||
prev_val = prev_ptr;
|
||||
} while (prev_ptr != node_);
|
||||
|
||||
if (thread_list_tail == node_) {
|
||||
thread_list_tail = tail_prev;
|
||||
}
|
||||
|
||||
prev->next = node_->next;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit KSynchronizationObject(KernelCore& kernel);
|
||||
~KSynchronizationObject() override;
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -56,6 +59,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
|
||||
public:
|
||||
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
|
||||
: KThreadQueueWithoutEndWait(kernel_) {}
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue {
|
||||
public:
|
||||
explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
|
||||
: KThreadQueue(kernel_), m_wait_list(wl) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
// Remove the thread from the wait list.
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
|
||||
private:
|
||||
KThread::WaiterList* m_wait_list;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
KThread::KThread(KernelCore& kernel_)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
|
||||
KThread::~KThread() = default;
|
||||
@@ -82,6 +113,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
[[fallthrough]];
|
||||
case ThreadType::HighPriority:
|
||||
[[fallthrough]];
|
||||
case ThreadType::Dummy:
|
||||
[[fallthrough]];
|
||||
case ThreadType::User:
|
||||
ASSERT(((owner == nullptr) ||
|
||||
(owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
|
||||
@@ -127,11 +160,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
priority = prio;
|
||||
base_priority = prio;
|
||||
|
||||
// Set sync object and waiting lock to null.
|
||||
synced_object = nullptr;
|
||||
|
||||
// Initialize sleeping queue.
|
||||
sleeping_queue = nullptr;
|
||||
wait_queue = nullptr;
|
||||
|
||||
// Set suspend flags.
|
||||
suspend_request_flags = 0;
|
||||
@@ -184,7 +214,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
// Setup the stack parameters.
|
||||
StackParameters& sp = GetStackParameters();
|
||||
sp.cur_thread = this;
|
||||
sp.disable_count = 1;
|
||||
sp.disable_count = 0;
|
||||
SetInExceptionHandler();
|
||||
|
||||
// Set thread ID.
|
||||
@@ -211,15 +241,16 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
|
||||
// Initialize the thread.
|
||||
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
|
||||
|
||||
// Initialize host context.
|
||||
// Initialize emulation parameters.
|
||||
thread->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
|
||||
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::InitializeDummyThread(KThread* thread) {
|
||||
return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main);
|
||||
return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
|
||||
}
|
||||
|
||||
ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
|
||||
@@ -273,11 +304,14 @@ void KThread::Finalize() {
|
||||
|
||||
auto it = waiter_list.begin();
|
||||
while (it != waiter_list.end()) {
|
||||
// The thread shouldn't be a kernel waiter.
|
||||
// Clear the lock owner
|
||||
it->SetLockOwner(nullptr);
|
||||
it->SetSyncedObject(nullptr, ResultInvalidState);
|
||||
it->Wakeup();
|
||||
|
||||
// Erase the waiter from our list.
|
||||
it = waiter_list.erase(it);
|
||||
|
||||
// Cancel the thread's wait.
|
||||
it->CancelWait(ResultInvalidState, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,15 +328,12 @@ bool KThread::IsSignaled() const {
|
||||
return signaled;
|
||||
}
|
||||
|
||||
void KThread::Wakeup() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
void KThread::OnTimer() {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// If we're waiting, cancel the wait.
|
||||
if (GetState() == ThreadState::Waiting) {
|
||||
if (sleeping_queue != nullptr) {
|
||||
sleeping_queue->WakeupThread(this);
|
||||
} else {
|
||||
SetState(ThreadState::Runnable);
|
||||
}
|
||||
wait_queue->CancelWait(this, ResultTimedOut, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +358,7 @@ void KThread::StartTermination() {
|
||||
|
||||
// Signal.
|
||||
signaled = true;
|
||||
NotifyAvailable();
|
||||
KSynchronizationObject::NotifyAvailable();
|
||||
|
||||
// Clear previous thread in KScheduler.
|
||||
KScheduler::ClearPreviousThread(kernel, this);
|
||||
@@ -475,30 +506,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
|
||||
ASSERT(parent != nullptr);
|
||||
ASSERT(v_affinity_mask != 0);
|
||||
KScopedLightLock lk{activity_pause_lock};
|
||||
KScopedLightLock lk(activity_pause_lock);
|
||||
|
||||
// Set the core mask.
|
||||
u64 p_affinity_mask = 0;
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
ASSERT(num_core_migration_disables >= 0);
|
||||
|
||||
// If the core id is no-update magic, preserve the ideal core id.
|
||||
if (cpu_core_id == Svc::IdealCoreNoUpdate) {
|
||||
cpu_core_id = virtual_ideal_core_id;
|
||||
R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination);
|
||||
// If we're updating, set our ideal virtual core.
|
||||
if (core_id_ != Svc::IdealCoreNoUpdate) {
|
||||
virtual_ideal_core_id = core_id_;
|
||||
} else {
|
||||
// Preserve our ideal core id.
|
||||
core_id_ = virtual_ideal_core_id;
|
||||
R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination);
|
||||
}
|
||||
|
||||
// Set the virtual core/affinity mask.
|
||||
virtual_ideal_core_id = cpu_core_id;
|
||||
// Set our affinity mask.
|
||||
virtual_affinity_mask = v_affinity_mask;
|
||||
|
||||
// Translate the virtual core to a physical core.
|
||||
if (cpu_core_id >= 0) {
|
||||
cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id];
|
||||
if (core_id_ >= 0) {
|
||||
core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_];
|
||||
}
|
||||
|
||||
// Translate the virtual affinity mask to a physical one.
|
||||
@@ -513,7 +546,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
const KAffinityMask old_mask = physical_affinity_mask;
|
||||
|
||||
// Set our new ideals.
|
||||
physical_ideal_core_id = cpu_core_id;
|
||||
physical_ideal_core_id = core_id_;
|
||||
physical_affinity_mask.SetAffinityMask(p_affinity_mask);
|
||||
|
||||
if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
|
||||
@@ -531,18 +564,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we edit the original affinity for restoration later.
|
||||
original_physical_ideal_core_id = cpu_core_id;
|
||||
original_physical_ideal_core_id = core_id_;
|
||||
original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the pinned waiter list.
|
||||
ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, std::addressof(pinned_waiter_list));
|
||||
{
|
||||
bool retry_update{};
|
||||
bool thread_is_pinned{};
|
||||
do {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Don't do any further management if our termination has been requested.
|
||||
R_SUCCEED_IF(IsTerminationRequested());
|
||||
@@ -570,12 +603,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
|
||||
ResultTerminationRequested);
|
||||
|
||||
// Note that the thread was pinned.
|
||||
thread_is_pinned = true;
|
||||
|
||||
// Wait until the thread isn't pinned any more.
|
||||
pinned_waiter_list.push_back(GetCurrentThread(kernel));
|
||||
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
|
||||
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
|
||||
} else {
|
||||
// If the thread isn't pinned, release the scheduler lock and retry until it's
|
||||
// not current.
|
||||
@@ -583,16 +613,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
}
|
||||
}
|
||||
} while (retry_update);
|
||||
|
||||
// If the thread was pinned, it no longer is, and we should remove the current thread from
|
||||
// our waiter list.
|
||||
if (thread_is_pinned) {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Remove from the list.
|
||||
pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
@@ -641,15 +661,9 @@ void KThread::WaitCancel() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Check if we're waiting and cancellable.
|
||||
if (GetState() == ThreadState::Waiting && cancellable) {
|
||||
if (sleeping_queue != nullptr) {
|
||||
sleeping_queue->WakeupThread(this);
|
||||
wait_cancelled = true;
|
||||
} else {
|
||||
SetSyncedObject(nullptr, ResultCancelled);
|
||||
SetState(ThreadState::Runnable);
|
||||
wait_cancelled = false;
|
||||
}
|
||||
if (this->GetState() == ThreadState::Waiting && cancellable) {
|
||||
wait_cancelled = false;
|
||||
wait_queue->CancelWait(this, ResultCancelled, true);
|
||||
} else {
|
||||
// Otherwise, note that we cancelled a wait.
|
||||
wait_cancelled = true;
|
||||
@@ -700,60 +714,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
|
||||
// Set the activity.
|
||||
{
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Verify our state.
|
||||
const auto cur_state = GetState();
|
||||
const auto cur_state = this->GetState();
|
||||
R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
|
||||
ResultInvalidState);
|
||||
|
||||
// Either pause or resume.
|
||||
if (activity == Svc::ThreadActivity::Paused) {
|
||||
// Verify that we're not suspended.
|
||||
R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
|
||||
R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
|
||||
|
||||
// Suspend.
|
||||
RequestSuspend(SuspendType::Thread);
|
||||
this->RequestSuspend(SuspendType::Thread);
|
||||
} else {
|
||||
ASSERT(activity == Svc::ThreadActivity::Runnable);
|
||||
|
||||
// Verify that we're suspended.
|
||||
R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
|
||||
R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
|
||||
|
||||
// Resume.
|
||||
Resume(SuspendType::Thread);
|
||||
this->Resume(SuspendType::Thread);
|
||||
}
|
||||
}
|
||||
|
||||
// If the thread is now paused, update the pinned waiter list.
|
||||
if (activity == Svc::ThreadActivity::Paused) {
|
||||
bool thread_is_pinned{};
|
||||
bool thread_is_current{};
|
||||
ThreadQueueImplForKThreadSetProperty wait_queue_(kernel,
|
||||
std::addressof(pinned_waiter_list));
|
||||
|
||||
bool thread_is_current;
|
||||
do {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Don't do any further management if our termination has been requested.
|
||||
R_SUCCEED_IF(IsTerminationRequested());
|
||||
R_SUCCEED_IF(this->IsTerminationRequested());
|
||||
|
||||
// By default, treat the thread as not current.
|
||||
thread_is_current = false;
|
||||
|
||||
// Check whether the thread is pinned.
|
||||
if (GetStackParameters().is_pinned) {
|
||||
if (this->GetStackParameters().is_pinned) {
|
||||
// Verify that the current thread isn't terminating.
|
||||
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
|
||||
ResultTerminationRequested);
|
||||
|
||||
// Note that the thread was pinned and not current.
|
||||
thread_is_pinned = true;
|
||||
thread_is_current = false;
|
||||
|
||||
// Wait until the thread isn't pinned any more.
|
||||
pinned_waiter_list.push_back(GetCurrentThread(kernel));
|
||||
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
|
||||
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
|
||||
} else {
|
||||
// Check if the thread is currently running.
|
||||
// If it is, we'll need to retry.
|
||||
thread_is_current = false;
|
||||
|
||||
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||
if (kernel.Scheduler(i).GetCurrentThread() == this) {
|
||||
thread_is_current = true;
|
||||
@@ -762,16 +775,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
|
||||
}
|
||||
}
|
||||
} while (thread_is_current);
|
||||
|
||||
// If the thread was pinned, it no longer is, and we should remove the current thread from
|
||||
// our waiter list.
|
||||
if (thread_is_pinned) {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Remove from the list.
|
||||
pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
@@ -966,6 +969,9 @@ ResultCode KThread::Run() {
|
||||
|
||||
// Set our state and finish.
|
||||
SetState(ThreadState::Runnable);
|
||||
|
||||
DisableDispatch();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
@@ -996,29 +1002,63 @@ ResultCode KThread::Sleep(s64 timeout) {
|
||||
ASSERT(this == GetCurrentThreadPointer(kernel));
|
||||
ASSERT(timeout > 0);
|
||||
|
||||
ThreadQueueImplForKThreadSleep wait_queue_(kernel);
|
||||
{
|
||||
// Setup the scheduling lock and sleep.
|
||||
KScopedSchedulerLockAndSleep slp{kernel, this, timeout};
|
||||
KScopedSchedulerLockAndSleep slp(kernel, this, timeout);
|
||||
|
||||
// Check if the thread should terminate.
|
||||
if (IsTerminationRequested()) {
|
||||
if (this->IsTerminationRequested()) {
|
||||
slp.CancelSleep();
|
||||
return ResultTerminationRequested;
|
||||
}
|
||||
|
||||
// Mark the thread as waiting.
|
||||
SetState(ThreadState::Waiting);
|
||||
// Wait for the sleep to end.
|
||||
this->BeginWait(std::addressof(wait_queue_));
|
||||
SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
|
||||
}
|
||||
|
||||
// The lock/sleep is done.
|
||||
|
||||
// Cancel the timer.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(this);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KThread::BeginWait(KThreadQueue* queue) {
|
||||
// Set our state as waiting.
|
||||
SetState(ThreadState::Waiting);
|
||||
|
||||
// Set our wait queue.
|
||||
wait_queue = queue;
|
||||
}
|
||||
|
||||
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// If we're waiting, notify our queue that we're available.
|
||||
if (GetState() == ThreadState::Waiting) {
|
||||
wait_queue->NotifyAvailable(this, signaled_object, wait_result_);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::EndWait(ResultCode wait_result_) {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// If we're waiting, notify our queue that we're available.
|
||||
if (GetState() == ThreadState::Waiting) {
|
||||
wait_queue->EndWait(this, wait_result_);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// If we're waiting, notify our queue that we're available.
|
||||
if (GetState() == ThreadState::Waiting) {
|
||||
wait_queue->CancelWait(this, wait_result_, cancel_timer_task);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::SetState(ThreadState state) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
@@ -1050,4 +1090,26 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
|
||||
return GetCurrentThread(kernel).GetCurrentCore();
|
||||
}
|
||||
|
||||
KScopedDisableDispatch::~KScopedDisableDispatch() {
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (kernel.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the reschedule if single-core, as dispatch tracking is disabled here.
|
||||
if (!Settings::values.use_multi_core.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
|
||||
auto scheduler = kernel.CurrentScheduler();
|
||||
|
||||
if (scheduler) {
|
||||
scheduler->RescheduleCurrentCore();
|
||||
}
|
||||
} else {
|
||||
GetCurrentThread(kernel).EnableDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -48,6 +48,7 @@ enum class ThreadType : u32 {
|
||||
Kernel = 1,
|
||||
HighPriority = 2,
|
||||
User = 3,
|
||||
Dummy = 100, // Special thread type for emulation purposes only
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
|
||||
|
||||
@@ -161,8 +162,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void Wakeup();
|
||||
|
||||
void SetBasePriority(s32 value);
|
||||
|
||||
[[nodiscard]] ResultCode Run();
|
||||
@@ -197,13 +196,19 @@ public:
|
||||
|
||||
void Suspend();
|
||||
|
||||
void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
|
||||
synced_object = obj;
|
||||
constexpr void SetSyncedIndex(s32 index) {
|
||||
synced_index = index;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s32 GetSyncedIndex() const {
|
||||
return synced_index;
|
||||
}
|
||||
|
||||
constexpr void SetWaitResult(ResultCode wait_res) {
|
||||
wait_result = wait_res;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
|
||||
*out = synced_object;
|
||||
[[nodiscard]] constexpr ResultCode GetWaitResult() const {
|
||||
return wait_result;
|
||||
}
|
||||
|
||||
@@ -374,6 +379,8 @@ public:
|
||||
|
||||
[[nodiscard]] bool IsSignaled() const override;
|
||||
|
||||
void OnTimer();
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
|
||||
@@ -446,20 +453,39 @@ public:
|
||||
return per_core_priority_queue_entry[core];
|
||||
}
|
||||
|
||||
void SetSleepingQueue(KThreadQueue* q) {
|
||||
sleeping_queue = q;
|
||||
[[nodiscard]] bool IsKernelThread() const {
|
||||
return GetActiveCore() == 3;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDispatchTrackingDisabled() const {
|
||||
return is_single_core || IsKernelThread();
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetDisableDispatchCount() const {
|
||||
if (IsDispatchTrackingDisabled()) {
|
||||
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this->GetStackParameters().disable_count;
|
||||
}
|
||||
|
||||
void DisableDispatch() {
|
||||
if (IsDispatchTrackingDisabled()) {
|
||||
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
|
||||
this->GetStackParameters().disable_count++;
|
||||
}
|
||||
|
||||
void EnableDispatch() {
|
||||
if (IsDispatchTrackingDisabled()) {
|
||||
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
@@ -573,6 +599,15 @@ public:
|
||||
address_key_value = val;
|
||||
}
|
||||
|
||||
void ClearWaitQueue() {
|
||||
wait_queue = nullptr;
|
||||
}
|
||||
|
||||
void BeginWait(KThreadQueue* queue);
|
||||
void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
|
||||
void EndWait(ResultCode wait_result_);
|
||||
void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
|
||||
|
||||
[[nodiscard]] bool HasWaiters() const {
|
||||
return !waiter_list.empty();
|
||||
}
|
||||
@@ -667,7 +702,6 @@ private:
|
||||
KAffinityMask physical_affinity_mask{};
|
||||
u64 thread_id{};
|
||||
std::atomic<s64> cpu_time{};
|
||||
KSynchronizationObject* synced_object{};
|
||||
VAddr address_key{};
|
||||
KProcess* parent{};
|
||||
VAddr kernel_stack_top{};
|
||||
@@ -677,13 +711,14 @@ private:
|
||||
s64 schedule_count{};
|
||||
s64 last_scheduled_tick{};
|
||||
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
|
||||
KThreadQueue* sleeping_queue{};
|
||||
KThreadQueue* wait_queue{};
|
||||
WaiterList waiter_list{};
|
||||
WaiterList pinned_waiter_list{};
|
||||
KThread* lock_owner{};
|
||||
u32 address_key_value{};
|
||||
u32 suspend_request_flags{};
|
||||
u32 suspend_allowed_flags{};
|
||||
s32 synced_index{};
|
||||
ResultCode wait_result{ResultSuccess};
|
||||
s32 base_priority{};
|
||||
s32 physical_ideal_core_id{};
|
||||
@@ -708,6 +743,7 @@ private:
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> host_context{};
|
||||
bool is_single_core{};
|
||||
|
||||
// For debugging
|
||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||
@@ -752,4 +788,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class KScopedDisableDispatch {
|
||||
public:
|
||||
[[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (kernel.IsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
GetCurrentThread(kernel).DisableDispatch();
|
||||
}
|
||||
|
||||
~KScopedDisableDispatch();
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
49
src/core/hle/kernel/k_thread_queue.cpp
Normal file
49
src/core/hle/kernel/k_thread_queue.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
|
||||
[[maybe_unused]] KSynchronizationObject* signaled_object,
|
||||
[[maybe_unused]] ResultCode wait_result) {}
|
||||
|
||||
void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
|
||||
// Set the thread's wait result.
|
||||
waiting_thread->SetWaitResult(wait_result);
|
||||
|
||||
// Set the thread as runnable.
|
||||
waiting_thread->SetState(ThreadState::Runnable);
|
||||
|
||||
// Clear the thread's wait queue.
|
||||
waiting_thread->ClearWaitQueue();
|
||||
|
||||
// Cancel the thread task.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
|
||||
}
|
||||
|
||||
void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task) {
|
||||
// Set the thread's wait result.
|
||||
waiting_thread->SetWaitResult(wait_result);
|
||||
|
||||
// Set the thread as runnable.
|
||||
waiting_thread->SetState(ThreadState::Runnable);
|
||||
|
||||
// Clear the thread's wait queue.
|
||||
waiting_thread->ClearWaitQueue();
|
||||
|
||||
// Cancel the thread task.
|
||||
if (cancel_timer_task) {
|
||||
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
|
||||
}
|
||||
}
|
||||
|
||||
void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
|
||||
[[maybe_unused]] ResultCode wait_result) {}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -11,71 +12,24 @@ namespace Kernel {
|
||||
class KThreadQueue {
|
||||
public:
|
||||
explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
virtual ~KThreadQueue() = default;
|
||||
|
||||
bool IsEmpty() const {
|
||||
return wait_list.empty();
|
||||
}
|
||||
|
||||
KThread::WaiterList::iterator begin() {
|
||||
return wait_list.begin();
|
||||
}
|
||||
KThread::WaiterList::iterator end() {
|
||||
return wait_list.end();
|
||||
}
|
||||
|
||||
bool SleepThread(KThread* t) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// If the thread needs terminating, don't enqueue it.
|
||||
if (t->IsTerminationRequested()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the thread's queue and mark it as waiting.
|
||||
t->SetSleepingQueue(this);
|
||||
t->SetState(ThreadState::Waiting);
|
||||
|
||||
// Add the thread to the queue.
|
||||
wait_list.push_back(*t);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WakeupThread(KThread* t) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Remove the thread from the queue.
|
||||
wait_list.erase(wait_list.iterator_to(*t));
|
||||
|
||||
// Mark the thread as no longer sleeping.
|
||||
t->SetState(ThreadState::Runnable);
|
||||
t->SetSleepingQueue(nullptr);
|
||||
}
|
||||
|
||||
KThread* WakeupFrontThread() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (wait_list.empty()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
// Remove the thread from the queue.
|
||||
auto it = wait_list.begin();
|
||||
KThread* thread = std::addressof(*it);
|
||||
wait_list.erase(it);
|
||||
|
||||
ASSERT(thread->GetState() == ThreadState::Waiting);
|
||||
|
||||
// Mark the thread as no longer sleeping.
|
||||
thread->SetState(ThreadState::Runnable);
|
||||
thread->SetSleepingQueue(nullptr);
|
||||
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
|
||||
ResultCode wait_result);
|
||||
virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
|
||||
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
|
||||
bool cancel_timer_task);
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
KThread::WaiterList wait_list{};
|
||||
};
|
||||
|
||||
class KThreadQueueWithoutEndWait : public KThreadQueue {
|
||||
public:
|
||||
explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
|
||||
|
||||
void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/thread_worker.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
@@ -83,12 +84,16 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void InitializeCores() {
|
||||
for (auto& core : cores) {
|
||||
core.Initialize(current_process->Is64BitProcess());
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
cores[core_id].Initialize(current_process->Is64BitProcess());
|
||||
system.Memory().SetCurrentPageTable(*current_process, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
is_shutting_down.store(true, std::memory_order_relaxed);
|
||||
SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
|
||||
|
||||
process_list.clear();
|
||||
|
||||
// Close all open server ports.
|
||||
@@ -123,15 +128,6 @@ struct KernelCore::Impl {
|
||||
next_user_process_id = KProcess::ProcessIDMin;
|
||||
next_thread_id = 1;
|
||||
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
if (suspend_threads[core_id]) {
|
||||
suspend_threads[core_id]->Close();
|
||||
suspend_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
schedulers[core_id].reset();
|
||||
}
|
||||
|
||||
cores.clear();
|
||||
|
||||
global_handle_table->Finalize();
|
||||
@@ -159,6 +155,16 @@ struct KernelCore::Impl {
|
||||
CleanupObject(time_shared_mem);
|
||||
CleanupObject(system_resource_limit);
|
||||
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
if (suspend_threads[core_id]) {
|
||||
suspend_threads[core_id]->Close();
|
||||
suspend_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
schedulers[core_id]->Finalize();
|
||||
schedulers[core_id].reset();
|
||||
}
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
|
||||
|
||||
@@ -245,13 +251,11 @@ struct KernelCore::Impl {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
global_scheduler_context->PreemptThreads();
|
||||
}
|
||||
const auto time_interval = std::chrono::nanoseconds{
|
||||
Core::Timing::msToCycles(std::chrono::milliseconds(10))};
|
||||
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
});
|
||||
|
||||
const auto time_interval =
|
||||
std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
|
||||
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
}
|
||||
|
||||
@@ -267,14 +271,6 @@ struct KernelCore::Impl {
|
||||
|
||||
void MakeCurrentProcess(KProcess* process) {
|
||||
current_process = process;
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 core_id = GetCurrentHostThreadID();
|
||||
if (core_id < Core::Hardware::NUM_CPU_CORES) {
|
||||
system.Memory().SetCurrentPageTable(*process, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline thread_local u32 host_thread_id = UINT32_MAX;
|
||||
@@ -344,7 +340,16 @@ struct KernelCore::Impl {
|
||||
is_phantom_mode_for_singlecore = value;
|
||||
}
|
||||
|
||||
bool IsShuttingDown() const {
|
||||
return is_shutting_down.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
KThread* GetCurrentEmuThread() {
|
||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||
if (IsShuttingDown()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto thread_id = GetCurrentHostThreadID();
|
||||
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return GetHostDummyThread();
|
||||
@@ -760,6 +765,7 @@ struct KernelCore::Impl {
|
||||
std::vector<std::unique_ptr<KThread>> dummy_threads;
|
||||
|
||||
bool is_multicore{};
|
||||
std::atomic_bool is_shutting_down{};
|
||||
bool is_phantom_mode_for_singlecore{};
|
||||
u32 single_core_thread_id{};
|
||||
|
||||
@@ -845,16 +851,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
|
||||
return impl->cores[id];
|
||||
}
|
||||
|
||||
size_t KernelCore::CurrentPhysicalCoreIndex() const {
|
||||
const u32 core_id = impl->GetCurrentHostThreadID();
|
||||
if (core_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return Core::Hardware::NUM_CPU_CORES - 1;
|
||||
}
|
||||
return core_id;
|
||||
}
|
||||
|
||||
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
|
||||
u32 core_id = impl->GetCurrentHostThreadID();
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
return impl->cores[core_id];
|
||||
return impl->cores[CurrentPhysicalCoreIndex()];
|
||||
}
|
||||
|
||||
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
|
||||
u32 core_id = impl->GetCurrentHostThreadID();
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
return impl->cores[core_id];
|
||||
return impl->cores[CurrentPhysicalCoreIndex()];
|
||||
}
|
||||
|
||||
Kernel::KScheduler* KernelCore::CurrentScheduler() {
|
||||
@@ -1057,6 +1067,9 @@ void KernelCore::Suspend(bool in_suspention) {
|
||||
impl->suspend_threads[core_id]->SetState(state);
|
||||
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
|
||||
ThreadWaitReasonForDebugging::Suspended);
|
||||
if (!should_suspend) {
|
||||
impl->suspend_threads[core_id]->DisableDispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1065,19 +1078,21 @@ bool KernelCore::IsMulticore() const {
|
||||
return impl->is_multicore;
|
||||
}
|
||||
|
||||
bool KernelCore::IsShuttingDown() const {
|
||||
return impl->IsShuttingDown();
|
||||
}
|
||||
|
||||
void KernelCore::ExceptionalExit() {
|
||||
exception_exited = true;
|
||||
Suspend(true);
|
||||
}
|
||||
|
||||
void KernelCore::EnterSVCProfile() {
|
||||
std::size_t core = impl->GetCurrentHostThreadID();
|
||||
impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
|
||||
impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
|
||||
}
|
||||
|
||||
void KernelCore::ExitSVCProfile() {
|
||||
std::size_t core = impl->GetCurrentHostThreadID();
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
|
||||
|
||||
@@ -149,6 +149,9 @@ public:
|
||||
/// Gets the an instance of the respective physical CPU core.
|
||||
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
|
||||
|
||||
/// Gets the current physical core index for the running host thread.
|
||||
std::size_t CurrentPhysicalCoreIndex() const;
|
||||
|
||||
/// Gets the sole instance of the Scheduler at the current running core.
|
||||
Kernel::KScheduler* CurrentScheduler();
|
||||
|
||||
@@ -272,6 +275,8 @@ public:
|
||||
|
||||
bool IsMulticore() const;
|
||||
|
||||
bool IsShuttingDown() const;
|
||||
|
||||
void EnterSVCProfile();
|
||||
|
||||
void ExitSVCProfile();
|
||||
|
||||
@@ -25,24 +25,27 @@ public:
|
||||
void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
|
||||
|
||||
private:
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<std::jthread> threads;
|
||||
std::queue<std::function<void()>> requests;
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable condition;
|
||||
std::condition_variable_any condition;
|
||||
const std::string service_name;
|
||||
bool stop{};
|
||||
};
|
||||
|
||||
ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
|
||||
: service_name{name} {
|
||||
for (std::size_t i = 0; i < num_threads; ++i)
|
||||
threads.emplace_back([this, &kernel] {
|
||||
for (std::size_t i = 0; i < num_threads; ++i) {
|
||||
threads.emplace_back([this, &kernel](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
|
||||
|
||||
// Wait for first request before trying to acquire a render context
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||
condition.wait(lock, stop_token, [this] { return !requests.empty(); });
|
||||
}
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
kernel.RegisterHostThread();
|
||||
@@ -52,10 +55,16 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
|
||||
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||
if (stop || requests.empty()) {
|
||||
condition.wait(lock, stop_token, [this] { return !requests.empty(); });
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requests.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
task = std::move(requests.front());
|
||||
requests.pop();
|
||||
}
|
||||
@@ -63,6 +72,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
|
||||
task();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceThread::Impl::QueueSyncRequest(KSession& session,
|
||||
@@ -88,12 +98,9 @@ void ServiceThread::Impl::QueueSyncRequest(KSession& session,
|
||||
}
|
||||
|
||||
ServiceThread::Impl::~Impl() {
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
stop = true;
|
||||
}
|
||||
condition.notify_all();
|
||||
for (std::thread& thread : threads) {
|
||||
for (auto& thread : threads) {
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -308,26 +309,29 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
|
||||
|
||||
/// Makes a blocking IPC call to an OS service.
|
||||
static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Create the wait queue.
|
||||
KThreadQueue wait_queue(kernel);
|
||||
|
||||
// Get the client session from its handle.
|
||||
KScopedAutoObject session =
|
||||
kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||
|
||||
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
thread->SetState(ThreadState::Waiting);
|
||||
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
|
||||
{
|
||||
KScopedAutoObject session =
|
||||
kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||
session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
|
||||
}
|
||||
// This is a synchronous request, so we should wait for our request to complete.
|
||||
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
|
||||
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
|
||||
}
|
||||
|
||||
KSynchronizationObject* dummy{};
|
||||
return thread->GetWaitResult(std::addressof(dummy));
|
||||
return thread->GetWaitResult();
|
||||
}
|
||||
|
||||
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
|
||||
@@ -874,7 +878,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||
const u64 thread_ticks = current_thread->GetCpuTime();
|
||||
|
||||
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
@@ -888,7 +892,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||
return ResultInvalidHandle;
|
||||
}
|
||||
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) {
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
|
||||
info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
@@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||
Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
|
||||
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
|
||||
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
|
||||
thread->Wakeup();
|
||||
{
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
thread->OnTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ private:
|
||||
libusb_device_handle* handle{};
|
||||
};
|
||||
|
||||
GCAdapter::GCAdapter(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
GCAdapter::GCAdapter(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
if (usb_adapter_handle) {
|
||||
return;
|
||||
}
|
||||
@@ -325,8 +325,8 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Input::VibrationError GCAdapter::SetRumble(const PadIdentifier& identifier,
|
||||
const Common::Input::VibrationStatus vibration) {
|
||||
Common::Input::VibrationError GCAdapter::SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
|
||||
const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
|
||||
const auto processed_amplitude =
|
||||
static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace InputCommon {
|
||||
class LibUSBContext;
|
||||
class LibUSBDeviceHandle;
|
||||
|
||||
class GCAdapter : public InputCommon::InputEngine {
|
||||
class GCAdapter : public InputEngine {
|
||||
public:
|
||||
explicit GCAdapter(const std::string& input_engine_);
|
||||
~GCAdapter();
|
||||
explicit GCAdapter(std::string input_engine_);
|
||||
~GCAdapter() override;
|
||||
|
||||
Common::Input::VibrationError SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
|
||||
@@ -24,7 +24,7 @@ constexpr PadIdentifier keyboard_modifier_identifier = {
|
||||
.pad = 1,
|
||||
};
|
||||
|
||||
Keyboard::Keyboard(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
// Keyboard is broken into 3 diferent sets:
|
||||
// key: Unfiltered intended for controllers.
|
||||
// keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace InputCommon {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Keyboard final : public InputCommon::InputEngine {
|
||||
class Keyboard final : public InputEngine {
|
||||
public:
|
||||
explicit Keyboard(const std::string& input_engine_);
|
||||
explicit Keyboard(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
|
||||
@@ -24,7 +24,7 @@ constexpr PadIdentifier identifier = {
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Mouse::Mouse(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, mouse_axis_x);
|
||||
PreSetAxis(identifier, mouse_axis_y);
|
||||
|
||||
@@ -27,9 +27,9 @@ enum class MouseButton {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Mouse final : public InputCommon::InputEngine {
|
||||
class Mouse final : public InputEngine {
|
||||
public:
|
||||
explicit Mouse(const std::string& input_engine_);
|
||||
explicit Mouse(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicMotion GetMotion() {
|
||||
const BasicMotion& GetMotion() const {
|
||||
return motion;
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
if (joystick->UpdateMotion(event.csensor)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetMotion(identifier, 0, joystick->GetMotion());
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -387,7 +387,7 @@ void SDLDriver::CloseJoysticks() {
|
||||
joystick_map.clear();
|
||||
}
|
||||
|
||||
SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
if (!Settings::values.enable_raw_input) {
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
@@ -403,10 +403,11 @@ SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engin
|
||||
|
||||
// Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and
|
||||
// not a generic one
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
|
||||
// Turn off Pro controller home led
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
|
||||
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
|
||||
// driver on Linux.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
|
||||
|
||||
// If the frontend is going to manage the event loop, then we don't start one here
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0;
|
||||
@@ -491,8 +492,9 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
Common::Input::VibrationError SDLDriver::SetRumble(const PadIdentifier& identifier,
|
||||
const Common::Input::VibrationStatus vibration) {
|
||||
|
||||
Common::Input::VibrationError SDLDriver::SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
|
||||
const auto joystick =
|
||||
GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port));
|
||||
const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
|
||||
@@ -526,6 +528,7 @@ Common::Input::VibrationError SDLDriver::SetRumble(const PadIdentifier& identifi
|
||||
|
||||
return Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
|
||||
s32 axis, float value) const {
|
||||
Common::ParamPackage params{};
|
||||
|
||||
@@ -19,19 +19,19 @@ using SDL_GameController = struct _SDL_GameController;
|
||||
using SDL_Joystick = struct _SDL_Joystick;
|
||||
using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
using ButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
|
||||
using ZButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
class SDLDriver : public InputCommon::InputEngine {
|
||||
class SDLDriver : public InputEngine {
|
||||
public:
|
||||
/// Initializes and registers SDL device factories
|
||||
SDLDriver(const std::string& input_engine_);
|
||||
explicit SDLDriver(std::string input_engine_);
|
||||
|
||||
/// Unregisters SDL device factories and shut them down.
|
||||
~SDLDriver() override;
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
u8 GetHatButtonId(const std::string& direction_name) const override;
|
||||
|
||||
Common::Input::VibrationError SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
private:
|
||||
void InitJoystick(int joystick_index);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
|
||||
enum TasAxes : u8 {
|
||||
enum class Tas::TasAxis : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
@@ -47,7 +46,7 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
Tas::Tas(const std::string& input_engine_) : InputCommon::InputEngine(input_engine_) {
|
||||
Tas::Tas(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) {
|
||||
PadIdentifier identifier{
|
||||
.guid = Common::UUID{},
|
||||
@@ -66,7 +65,7 @@ Tas::Tas(const std::string& input_engine_) : InputCommon::InputEngine(input_engi
|
||||
|
||||
Tas::~Tas() {
|
||||
Stop();
|
||||
};
|
||||
}
|
||||
|
||||
void Tas::LoadTasFiles() {
|
||||
script_length = 0;
|
||||
@@ -79,43 +78,43 @@ void Tas::LoadTasFiles() {
|
||||
}
|
||||
|
||||
void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
if (!commands[player_index].empty()) {
|
||||
commands[player_index].clear();
|
||||
}
|
||||
commands[player_index].clear();
|
||||
|
||||
std::string file = Common::FS::ReadStringFromFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
|
||||
fmt::format("script{}-{}.txt", file_index, player_index + 1),
|
||||
Common::FS::FileType::BinaryFile);
|
||||
std::stringstream command_line(file);
|
||||
std::istringstream command_line(file);
|
||||
std::string line;
|
||||
int frame_no = 0;
|
||||
while (std::getline(command_line, line, '\n')) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::smatch m;
|
||||
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ' ')) {
|
||||
seglist.push_back(segment);
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ' ')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
if (seglist.size() < 4) {
|
||||
if (seg_list.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (frame_no < std::stoi(seglist.at(0))) {
|
||||
commands[player_index].push_back({});
|
||||
const auto num_frames = std::stoi(seg_list[0]);
|
||||
while (frame_no < num_frames) {
|
||||
commands[player_index].emplace_back();
|
||||
frame_no++;
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
.buttons = ReadCommandButtons(seglist.at(1)),
|
||||
.l_axis = ReadCommandAxis(seglist.at(2)),
|
||||
.r_axis = ReadCommandAxis(seglist.at(3)),
|
||||
.buttons = ReadCommandButtons(seg_list[1]),
|
||||
.l_axis = ReadCommandAxis(seg_list[2]),
|
||||
.r_axis = ReadCommandAxis(seg_list[3]),
|
||||
};
|
||||
commands[player_index].push_back(command);
|
||||
frame_no++;
|
||||
@@ -123,16 +122,17 @@ void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
|
||||
}
|
||||
|
||||
void Tas::WriteTasFile(std::u8string file_name) {
|
||||
void Tas::WriteTasFile(std::u8string_view file_name) {
|
||||
std::string output_text;
|
||||
for (size_t frame = 0; frame < record_commands.size(); frame++) {
|
||||
const TASCommand& line = record_commands[frame];
|
||||
output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
|
||||
WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
|
||||
}
|
||||
const auto bytes_written = Common::FS::WriteStringToFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
|
||||
Common::FS::FileType::TextFile, output_text);
|
||||
|
||||
const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name;
|
||||
const auto bytes_written =
|
||||
Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text);
|
||||
if (bytes_written == output_text.size()) {
|
||||
LOG_INFO(Input, "TAS file written to file!");
|
||||
} else {
|
||||
@@ -205,10 +205,10 @@ void Tas::UpdateThread() {
|
||||
const int button = static_cast<int>(i);
|
||||
SetButton(identifier, button, button_status);
|
||||
}
|
||||
SetAxis(identifier, TasAxes::StickX, command.l_axis.x);
|
||||
SetAxis(identifier, TasAxes::StickY, command.l_axis.y);
|
||||
SetAxis(identifier, TasAxes::SubstickX, command.r_axis.x);
|
||||
SetAxis(identifier, TasAxes::SubstickY, command.r_axis.y);
|
||||
SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y);
|
||||
SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y);
|
||||
}
|
||||
} else {
|
||||
is_running = Settings::values.tas_loop.GetValue();
|
||||
@@ -224,27 +224,28 @@ void Tas::ClearInput() {
|
||||
}
|
||||
|
||||
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ';')) {
|
||||
seglist.push_back(segment);
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ';')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
const float x = std::stof(seglist.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seglist.at(1)) / 32767.0f;
|
||||
const float x = std::stof(seg_list.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seg_list.at(1)) / 32767.0f;
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
u64 Tas::ReadCommandButtons(const std::string& data) const {
|
||||
std::stringstream button_text(data);
|
||||
std::string line;
|
||||
u64 Tas::ReadCommandButtons(const std::string& line) const {
|
||||
std::istringstream button_text(line);
|
||||
std::string button_line;
|
||||
u64 buttons = 0;
|
||||
while (std::getline(button_text, line, ';')) {
|
||||
for (auto [text, tas_button] : text_to_tas_button) {
|
||||
if (text == line) {
|
||||
while (std::getline(button_text, button_line, ';')) {
|
||||
for (const auto& [text, tas_button] : text_to_tas_button) {
|
||||
if (text == button_line) {
|
||||
buttons |= static_cast<u64>(tas_button);
|
||||
break;
|
||||
}
|
||||
@@ -254,8 +255,8 @@ u64 Tas::ReadCommandButtons(const std::string& data) const {
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandButtons(u64 buttons) const {
|
||||
std::string returns = "";
|
||||
for (auto [text_button, tas_button] : text_to_tas_button) {
|
||||
std::string returns;
|
||||
for (const auto& [text_button, tas_button] : text_to_tas_button) {
|
||||
if ((buttons & static_cast<u64>(tas_button)) != 0) {
|
||||
returns += fmt::format("{};", text_button);
|
||||
}
|
||||
@@ -267,6 +268,10 @@ std::string Tas::WriteCommandAxis(TasAnalog analog) const {
|
||||
return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
|
||||
}
|
||||
|
||||
void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) {
|
||||
SetAxis(identifier, static_cast<int>(axis), value);
|
||||
}
|
||||
|
||||
void Tas::StartStop() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
/*
|
||||
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
|
||||
@@ -81,46 +81,46 @@ enum class TasState {
|
||||
Stopped,
|
||||
};
|
||||
|
||||
class Tas final : public InputCommon::InputEngine {
|
||||
class Tas final : public InputEngine {
|
||||
public:
|
||||
explicit Tas(const std::string& input_engine_);
|
||||
~Tas();
|
||||
explicit Tas(std::string input_engine_);
|
||||
~Tas() override;
|
||||
|
||||
/**
|
||||
* Changes the input status that will be stored in each frame
|
||||
* @param buttons: bitfield with the status of the buttons
|
||||
* @param left_axis: value of the left axis
|
||||
* @param right_axis: value of the right axis
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @param left_axis Value of the left axis
|
||||
* @param right_axis Value of the right axis
|
||||
*/
|
||||
void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
|
||||
|
||||
// Main loop that records or executes input
|
||||
void UpdateThread();
|
||||
|
||||
// Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
|
||||
// Sets the flag to start or stop the TAS command execution and swaps controllers profiles
|
||||
void StartStop();
|
||||
|
||||
// Stop the TAS and reverts any controller profile
|
||||
// Stop the TAS and reverts any controller profile
|
||||
void Stop();
|
||||
|
||||
// Sets the flag to reload the file and start from the begining in the next update
|
||||
// Sets the flag to reload the file and start from the beginning in the next update
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Sets the flag to enable or disable recording of inputs
|
||||
* @return Returns true if the current recording status is enabled
|
||||
* @returns true if the current recording status is enabled
|
||||
*/
|
||||
bool Record();
|
||||
|
||||
/**
|
||||
* Saves contents of record_commands on a file
|
||||
* @param overwrite_file: Indicates if player 1 should be overwritten
|
||||
* @param overwrite_file Indicates if player 1 should be overwritten
|
||||
*/
|
||||
void SaveRecording(bool overwrite_file);
|
||||
|
||||
/**
|
||||
* Returns the current status values of TAS playback/recording
|
||||
* @return Tuple of
|
||||
* @returns A Tuple of
|
||||
* TasState indicating the current state out of Running ;
|
||||
* Current playback progress ;
|
||||
* Total length of script file currently loaded or being recorded
|
||||
@@ -128,6 +128,8 @@ public:
|
||||
std::tuple<TasState, size_t, size_t> GetStatus() const;
|
||||
|
||||
private:
|
||||
enum class TasAxis : u8;
|
||||
|
||||
struct TASCommand {
|
||||
u64 buttons{};
|
||||
TasAnalog l_axis{};
|
||||
@@ -137,29 +139,31 @@ private:
|
||||
/// Loads TAS files from all players
|
||||
void LoadTasFiles();
|
||||
|
||||
/** Loads TAS file from the specified player
|
||||
* @param player_index: player number to save the script
|
||||
* @param file_index: script number of the file
|
||||
/**
|
||||
* Loads TAS file from the specified player
|
||||
* @param player_index Player number to save the script
|
||||
* @param file_index Script number of the file
|
||||
*/
|
||||
void LoadTasFile(size_t player_index, size_t file_index);
|
||||
|
||||
/** Writes a TAS file from the recorded commands
|
||||
* @param file_name: name of the file to be written
|
||||
/**
|
||||
* Writes a TAS file from the recorded commands
|
||||
* @param file_name Name of the file to be written
|
||||
*/
|
||||
void WriteTasFile(std::u8string file_name);
|
||||
void WriteTasFile(std::u8string_view file_name);
|
||||
|
||||
/**
|
||||
* Parses a string containing the axis values. X and Y have a range from -32767 to 32767
|
||||
* @param line: string containing axis values with the following format "x;y"
|
||||
* @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
|
||||
* @param line String containing axis values with the following format "x;y"
|
||||
* @returns A TAS analog object with axis values with range from -1.0 to 1.0
|
||||
*/
|
||||
TasAnalog ReadCommandAxis(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Parses a string containing the button values. Each button is represented by it's text format
|
||||
* specified in text_to_tas_button array
|
||||
* @param line: string containing button name with the following format "a;b;c;d..."
|
||||
* @return Returns a u64 with each bit representing the status of a button
|
||||
* @param line string containing button name with the following format "a;b;c;d..."
|
||||
* @returns A u64 with each bit representing the status of a button
|
||||
*/
|
||||
u64 ReadCommandButtons(const std::string& line) const;
|
||||
|
||||
@@ -170,17 +174,20 @@ private:
|
||||
|
||||
/**
|
||||
* Converts an u64 containing the button status into the text equivalent
|
||||
* @param buttons: bitfield with the status of the buttons
|
||||
* @return Returns a string with the name of the buttons to be written to the file
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @returns A string with the name of the buttons to be written to the file
|
||||
*/
|
||||
std::string WriteCommandButtons(u64 buttons) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @param data: value of the axis
|
||||
* @return A string with the value of the axis to be written to the file
|
||||
* @param analog Value of the axis
|
||||
* @returns A string with the value of the axis to be written to the file
|
||||
*/
|
||||
std::string WriteCommandAxis(TasAnalog data) const;
|
||||
std::string WriteCommandAxis(TasAnalog analog) const;
|
||||
|
||||
/// Sets an axis for a particular pad to the given value.
|
||||
void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
|
||||
|
||||
size_t script_length{0};
|
||||
bool is_recording{false};
|
||||
|
||||
@@ -13,7 +13,7 @@ constexpr PadIdentifier identifier = {
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
TouchScreen::TouchScreen(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace InputCommon {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputCommon::InputEngine {
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(const std::string& input_engine_);
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
|
||||
@@ -136,7 +136,7 @@ static void SocketLoop(Socket* socket) {
|
||||
socket->Loop();
|
||||
}
|
||||
|
||||
UDPClient::UDPClient(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
UDPClient::UDPClient(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
LOG_INFO(Input, "Udp Initialization started");
|
||||
ReloadSockets();
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ struct DeviceStatus {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class UDPClient final : public InputCommon::InputEngine {
|
||||
class UDPClient final : public InputEngine {
|
||||
public:
|
||||
explicit UDPClient(const std::string& input_engine_);
|
||||
~UDPClient();
|
||||
explicit UDPClient(std::string input_engine_);
|
||||
~UDPClient() override;
|
||||
|
||||
void ReloadSockets();
|
||||
|
||||
|
||||
@@ -19,23 +19,36 @@ public:
|
||||
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
|
||||
right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
|
||||
modifier_angle(modifier_angle_) {
|
||||
Common::Input::InputCallback button_up_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateUpButtonStatus(callback_); }};
|
||||
Common::Input::InputCallback button_down_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateDownButtonStatus(callback_); }};
|
||||
Common::Input::InputCallback button_left_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateLeftButtonStatus(callback_); }};
|
||||
Common::Input::InputCallback button_right_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) {
|
||||
UpdateRightButtonStatus(callback_);
|
||||
}};
|
||||
Common::Input::InputCallback button_modifier_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateModButtonStatus(callback_); }};
|
||||
up->SetCallback(button_up_callback);
|
||||
down->SetCallback(button_down_callback);
|
||||
left->SetCallback(button_left_callback);
|
||||
right->SetCallback(button_right_callback);
|
||||
modifier->SetCallback(button_modifier_callback);
|
||||
up->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateUpButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
down->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateDownButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
left->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateLeftButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
right->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateRightButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
modifier->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateModButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
last_x_axis_value = 0.0f;
|
||||
last_y_axis_value = 0.0f;
|
||||
}
|
||||
@@ -133,27 +146,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateUpButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
up_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateDownButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
down_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateLeftButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
left_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateRightButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
right_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateModButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
modifier_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
@@ -265,18 +278,18 @@ private:
|
||||
Button left;
|
||||
Button right;
|
||||
Button modifier;
|
||||
float modifier_scale;
|
||||
float modifier_angle;
|
||||
float modifier_scale{};
|
||||
float modifier_angle{};
|
||||
float angle{};
|
||||
float goal_angle{};
|
||||
float amplitude{};
|
||||
bool up_status;
|
||||
bool down_status;
|
||||
bool left_status;
|
||||
bool right_status;
|
||||
bool modifier_status;
|
||||
float last_x_axis_value;
|
||||
float last_y_axis_value;
|
||||
bool up_status{};
|
||||
bool down_status{};
|
||||
bool left_status{};
|
||||
bool right_status{};
|
||||
bool modifier_status{};
|
||||
float last_x_axis_value{};
|
||||
float last_y_axis_value{};
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update;
|
||||
};
|
||||
|
||||
@@ -14,10 +14,13 @@ public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
|
||||
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
|
||||
Common::Input::InputCallback button_up_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateButtonStatus(callback_); }};
|
||||
last_button_value = false;
|
||||
button->SetCallback(button_up_callback);
|
||||
button->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
button->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -47,7 +50,7 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
void UpdateButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Touch,
|
||||
.touch_status = GetStatus(button_callback.button_status.value),
|
||||
|
||||
@@ -10,41 +10,31 @@ namespace InputCommon {
|
||||
|
||||
void InputEngine::PreSetController(const PadIdentifier& identifier) {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
controller_list.insert_or_assign(identifier, ControllerData{});
|
||||
}
|
||||
controller_list.try_emplace(identifier);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.buttons.contains(button)) {
|
||||
controller.buttons.insert_or_assign(button, false);
|
||||
}
|
||||
controller.buttons.try_emplace(button, false);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.hat_buttons.contains(button)) {
|
||||
controller.hat_buttons.insert_or_assign(button, u8{0});
|
||||
}
|
||||
controller.hat_buttons.try_emplace(button, u8{0});
|
||||
}
|
||||
|
||||
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.axes.contains(axis)) {
|
||||
controller.axes.insert_or_assign(axis, 0.0f);
|
||||
}
|
||||
controller.axes.try_emplace(axis, 0.0f);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.motions.contains(motion)) {
|
||||
controller.motions.insert_or_assign(motion, BasicMotion{});
|
||||
}
|
||||
controller.motions.try_emplace(motion);
|
||||
}
|
||||
|
||||
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
|
||||
@@ -91,7 +81,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value
|
||||
TriggerOnBatteryChange(identifier, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value) {
|
||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
@@ -104,85 +94,93 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMo
|
||||
|
||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
if (!controller.buttons.contains(button)) {
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto button_iter = controller.buttons.find(button);
|
||||
if (button_iter == controller.buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid button {}", button);
|
||||
return false;
|
||||
}
|
||||
return controller.buttons.at(button);
|
||||
return button_iter->second;
|
||||
}
|
||||
|
||||
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
if (!controller.hat_buttons.contains(button)) {
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto hat_iter = controller.hat_buttons.find(button);
|
||||
if (hat_iter == controller.hat_buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid hat button {}", button);
|
||||
return false;
|
||||
}
|
||||
return (controller.hat_buttons.at(button) & direction) != 0;
|
||||
return (hat_iter->second & direction) != 0;
|
||||
}
|
||||
|
||||
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return 0.0f;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
if (!controller.axes.contains(axis)) {
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto axis_iter = controller.axes.find(axis);
|
||||
if (axis_iter == controller.axes.cend()) {
|
||||
LOG_ERROR(Input, "Invalid axis {}", axis);
|
||||
return 0.0f;
|
||||
}
|
||||
return controller.axes.at(axis);
|
||||
return axis_iter->second;
|
||||
}
|
||||
|
||||
BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return BatteryLevel::Charging;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.battery;
|
||||
}
|
||||
|
||||
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.motions.at(motion);
|
||||
}
|
||||
|
||||
void InputEngine::ResetButtonState() {
|
||||
for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
|
||||
for (std::pair<int, bool> button : controller.second.buttons) {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& button : controller.second.buttons) {
|
||||
SetButton(controller.first, button.first, false);
|
||||
}
|
||||
for (std::pair<int, bool> button : controller.second.hat_buttons) {
|
||||
for (const auto& button : controller.second.hat_buttons) {
|
||||
SetHatButton(controller.first, button.first, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::ResetAnalogState() {
|
||||
for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
|
||||
for (std::pair<int, float> axis : controller.second.axes) {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& axis : controller.second.axes) {
|
||||
SetAxis(controller.first, axis.first, 0.0);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +188,7 @@ void InputEngine::ResetAnalogState() {
|
||||
|
||||
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
|
||||
continue;
|
||||
@@ -218,7 +216,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
|
||||
|
||||
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
|
||||
continue;
|
||||
@@ -247,7 +245,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
|
||||
|
||||
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
|
||||
continue;
|
||||
@@ -274,7 +272,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
|
||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] BatteryLevel value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
|
||||
continue;
|
||||
@@ -286,9 +284,9 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
BasicMotion value) {
|
||||
const BasicMotion& value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
|
||||
continue;
|
||||
@@ -342,7 +340,7 @@ const std::string& InputEngine::GetEngineName() const {
|
||||
|
||||
int InputEngine::SetCallback(InputIdentifier input_identifier) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
callback_list.insert_or_assign(last_callback_key, input_identifier);
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,15 @@ struct PadIdentifier {
|
||||
friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
|
||||
};
|
||||
|
||||
// Basic motion data containing data from the sensors and a timestamp in microsecons
|
||||
// Basic motion data containing data from the sensors and a timestamp in microseconds
|
||||
struct BasicMotion {
|
||||
float gyro_x;
|
||||
float gyro_y;
|
||||
float gyro_z;
|
||||
float accel_x;
|
||||
float accel_y;
|
||||
float accel_z;
|
||||
u64 delta_timestamp;
|
||||
float gyro_x{};
|
||||
float gyro_y{};
|
||||
float gyro_z{};
|
||||
float accel_x{};
|
||||
float accel_y{};
|
||||
float accel_z{};
|
||||
u64 delta_timestamp{};
|
||||
};
|
||||
|
||||
// Stages of a battery charge
|
||||
@@ -102,9 +102,7 @@ struct InputIdentifier {
|
||||
|
||||
class InputEngine {
|
||||
public:
|
||||
explicit InputEngine(const std::string& input_engine_) : input_engine(input_engine_) {
|
||||
callback_list.clear();
|
||||
}
|
||||
explicit InputEngine(std::string input_engine_) : input_engine{std::move(input_engine_)} {}
|
||||
|
||||
virtual ~InputEngine() = default;
|
||||
|
||||
@@ -116,14 +114,12 @@ public:
|
||||
|
||||
// Sets a led pattern for a controller
|
||||
virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::LedStatus led_status) {
|
||||
return;
|
||||
}
|
||||
[[maybe_unused]] const Common::Input::LedStatus& led_status) {}
|
||||
|
||||
// Sets rumble to a controller
|
||||
virtual Common::Input::VibrationError SetRumble(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus vibration) {
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
|
||||
return Common::Input::VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
@@ -140,36 +136,36 @@ public:
|
||||
/// Used for automapping features
|
||||
virtual std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the button mappings for the given device
|
||||
virtual InputCommon::ButtonMapping GetButtonMappingForDevice(
|
||||
virtual ButtonMapping GetButtonMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the analog mappings for the given device
|
||||
virtual InputCommon::AnalogMapping GetAnalogMappingForDevice(
|
||||
virtual AnalogMapping GetAnalogMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the motion mappings for the given device
|
||||
virtual InputCommon::MotionMapping GetMotionMappingForDevice(
|
||||
virtual MotionMapping GetMotionMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the name of the given input.
|
||||
virtual Common::Input::ButtonNames GetUIName(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) const {
|
||||
return Common::Input::ButtonNames::Engine;
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the index number of the given hat button direction
|
||||
virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
void PreSetController(const PadIdentifier& identifier);
|
||||
void PreSetButton(const PadIdentifier& identifier, int button);
|
||||
@@ -194,7 +190,7 @@ protected:
|
||||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
|
||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||
|
||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||
return "Unknown";
|
||||
@@ -206,14 +202,15 @@ private:
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
BatteryLevel battery;
|
||||
BatteryLevel battery{};
|
||||
};
|
||||
|
||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int button, f32 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, BasicMotion value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value);
|
||||
|
||||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||
const PadIdentifier& identifier, EngineInputType type,
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
MappingFactory();
|
||||
|
||||
/**
|
||||
* Resets all varables to beggin the mapping process
|
||||
* @param "type": type of input desired to be returned
|
||||
* Resets all variables to begin the mapping process
|
||||
* @param type type of input desired to be returned
|
||||
*/
|
||||
void BeginMapping(Polling::InputType type);
|
||||
|
||||
@@ -24,8 +24,8 @@ public:
|
||||
|
||||
/**
|
||||
* Registers mapping input data from the driver
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterInput(const MappingData& data);
|
||||
|
||||
@@ -34,42 +34,42 @@ public:
|
||||
|
||||
private:
|
||||
/**
|
||||
* If provided data satisfies the requeriments it will push an element to the input_queue
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button: Creates a basic button ParamPackage
|
||||
* - HatButton: Creates a basic hat button ParamPackage
|
||||
* - Analog: Creates a basic analog ParamPackage
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterButton(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requeriments it will push an element to the input_queue
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterStick(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requeriments it will push an element to the input_queue
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first two axis and on the third axis creates a basic Motion
|
||||
* ParamPackage
|
||||
* - Motion: Creates a basic Motion ParamPackage
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterMotion(const MappingData& data);
|
||||
|
||||
/**
|
||||
* Returns true if driver can be mapped
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
bool IsDriverValid(const MappingData& data) const;
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace InputCommon {
|
||||
|
||||
class DummyInput final : public Common::Input::InputDevice {
|
||||
public:
|
||||
explicit DummyInput() {}
|
||||
~DummyInput() {}
|
||||
explicit DummyInput() = default;
|
||||
};
|
||||
|
||||
class InputFromButton final : public Common::Input::InputDevice {
|
||||
@@ -33,7 +32,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromButton() {
|
||||
~InputFromButton() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Button,
|
||||
.button_status = GetStatus(),
|
||||
@@ -94,7 +93,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromHatButton() {
|
||||
~InputFromHatButton() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -106,7 +105,7 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Button,
|
||||
.button_status = GetStatus(),
|
||||
@@ -167,7 +166,7 @@ public:
|
||||
callback_key_y = input_engine->SetCallback(y_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromStick() {
|
||||
~InputFromStick() override {
|
||||
input_engine->DeleteCallback(callback_key_x);
|
||||
input_engine->DeleteCallback(callback_key_y);
|
||||
}
|
||||
@@ -190,7 +189,7 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Stick,
|
||||
.stick_status = GetStatus(),
|
||||
@@ -266,7 +265,7 @@ public:
|
||||
callback_key_y = input_engine->SetCallback(y_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromTouch() {
|
||||
~InputFromTouch() override {
|
||||
input_engine->DeleteCallback(callback_key_button);
|
||||
input_engine->DeleteCallback(callback_key_x);
|
||||
input_engine->DeleteCallback(callback_key_y);
|
||||
@@ -352,7 +351,7 @@ public:
|
||||
axis_callback_key = input_engine->SetCallback(axis_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromTrigger() {
|
||||
~InputFromTrigger() override {
|
||||
input_engine->DeleteCallback(callback_key_button);
|
||||
input_engine->DeleteCallback(axis_callback_key);
|
||||
}
|
||||
@@ -419,7 +418,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromAnalog() {
|
||||
~InputFromAnalog() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -466,7 +465,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromBattery() {
|
||||
~InputFromBattery() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -474,7 +473,7 @@ public:
|
||||
return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier));
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Battery,
|
||||
.battery_status = GetStatus(),
|
||||
@@ -518,7 +517,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromMotion() {
|
||||
~InputFromMotion() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -593,7 +592,7 @@ public:
|
||||
callback_key_z = input_engine->SetCallback(z_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromAxisMotion() {
|
||||
~InputFromAxisMotion() override {
|
||||
input_engine->DeleteCallback(callback_key_x);
|
||||
input_engine->DeleteCallback(callback_key_y);
|
||||
input_engine->DeleteCallback(callback_key_z);
|
||||
@@ -618,7 +617,7 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Motion,
|
||||
.motion_status = GetStatus(),
|
||||
@@ -668,16 +667,16 @@ public:
|
||||
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
|
||||
: identifier(identifier_), input_engine(input_engine_) {}
|
||||
|
||||
virtual void SetLED(Common::Input::LedStatus led_status) {
|
||||
void SetLED(const Common::Input::LedStatus& led_status) override {
|
||||
input_engine->SetLeds(identifier, led_status);
|
||||
}
|
||||
|
||||
virtual Common::Input::VibrationError SetVibration(
|
||||
Common::Input::VibrationStatus vibration_status) {
|
||||
Common::Input::VibrationError SetVibration(
|
||||
const Common::Input::VibrationStatus& vibration_status) override {
|
||||
return input_engine->SetRumble(identifier, vibration_status);
|
||||
}
|
||||
|
||||
virtual Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
|
||||
return input_engine->SetPollingMode(identifier, polling_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,6 @@ class Factory;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputEngine;
|
||||
/**
|
||||
* An Input factory. It receives input events and forward them to all input devices it created.
|
||||
*/
|
||||
|
||||
class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
|
||||
public:
|
||||
@@ -24,10 +21,10 @@ public:
|
||||
/**
|
||||
* Creates an output device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique ouput device with the parameters specified
|
||||
* - "guid" text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique output device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::OutputDevice> Create(
|
||||
const Common::ParamPackage& params) override;
|
||||
@@ -36,6 +33,9 @@ private:
|
||||
std::shared_ptr<InputEngine> input_engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* An Input factory. It receives input events and forward them to all input devices it created.
|
||||
*/
|
||||
class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
public:
|
||||
explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);
|
||||
@@ -54,16 +54,16 @@ public:
|
||||
* - battery: Contains "battery"
|
||||
* - output: Contains "output"
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "code": the code of the keyboard key to bind with the input
|
||||
* @param - "button": same as "code" but for controller buttons
|
||||
* @param - "hat": similar as "button" but it's a group of hat buttons from SDL
|
||||
* @param - "axis": the axis number of the axis to bind with the input
|
||||
* @param - "motion": the motion number of the motion to bind with the input
|
||||
* @param - "axis_x": same as axis but specifing horizontal direction
|
||||
* @param - "axis_y": same as axis but specifing vertical direction
|
||||
* @param - "axis_z": same as axis but specifing forward direction
|
||||
* @param - "battery": Only used as a placeholder to set the input type
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "hat": similar as "button" but it's a group of hat buttons from SDL
|
||||
* - "axis": the axis number of the axis to bind with the input
|
||||
* - "motion": the motion number of the motion to bind with the input
|
||||
* - "axis_x": same as axis but specifying horizontal direction
|
||||
* - "axis_y": same as axis but specifying vertical direction
|
||||
* - "axis_z": same as axis but specifying forward direction
|
||||
* - "battery": Only used as a placeholder to set the input type
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
@@ -71,14 +71,14 @@ private:
|
||||
/**
|
||||
* Creates a button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "code": the code of the keyboard key to bind with the input
|
||||
* @param - "button": same as "code" but for controller buttons
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -86,14 +86,14 @@ private:
|
||||
/**
|
||||
* Creates a hat button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "button": the controller hat id to bind with the input
|
||||
* @param - "direction": the direction id to be detected
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -101,19 +101,19 @@ private:
|
||||
/**
|
||||
* Creates a stick device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* @param - "axis_y": the controller vertical axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset_x": the amount of offset in the x axis
|
||||
* @param - "offset_y": the amount of offset in the y axis
|
||||
* @param - "invert_x": inverts the sign of the horizontal axis
|
||||
* @param - "invert_y": inverts the sign of the vertical axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -121,16 +121,16 @@ private:
|
||||
/**
|
||||
* Creates an analog device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "axis": the controller axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset": the amount of offset in the axis
|
||||
* @param - "invert": inverts the sign of the axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -138,20 +138,20 @@ private:
|
||||
/**
|
||||
* Creates a trigger device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "button": the controller hat id to bind with the input
|
||||
* @param - "direction": the direction id to be detected
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "axis": the controller axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset": the amount of offset in the axis
|
||||
* @param - "invert": inverts the sign of the axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -159,23 +159,23 @@ private:
|
||||
/**
|
||||
* Creates a touch device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "button": the controller hat id to bind with the input
|
||||
* @param - "direction": the direction id to be detected
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* @param - "axis_y": the controller vertical axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset_x": the amount of offset in the x axis
|
||||
* @param - "offset_y": the amount of offset in the y axis
|
||||
* @param - "invert_x": inverts the sign of the horizontal axis
|
||||
* @param - "invert_y": inverts the sign of the vertical axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -183,10 +183,10 @@ private:
|
||||
/**
|
||||
* Creates a battery device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -194,21 +194,21 @@ private:
|
||||
/**
|
||||
* Creates a motion device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* @param - "axis_y": the controller vertical axis id to bind with the input
|
||||
* @param - "axis_z": the controller fordward axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "offset_x": the amount of offset in the x axis
|
||||
* @param - "offset_y": the amount of offset in the y axis
|
||||
* @param - "offset_z": the amount of offset in the z axis
|
||||
* @param - "invert_x": inverts the sign of the horizontal axis
|
||||
* @param - "invert_y": inverts the sign of the vertical axis
|
||||
* @param - "invert_z": inverts the sign of the fordward axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "axis_z": the controller forward axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "offset_z": the amount of offset in the z axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "invert_z": inverts the sign of the forward axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -98,10 +98,6 @@ void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
u32 TexCoordIndex(IR::Attribute attr) {
|
||||
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
@@ -190,18 +186,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
// GLSL only exposes 8 legacy texcoords
|
||||
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
|
||||
TexCoordIndex(attr));
|
||||
ctx.AddF32("{}=0.f;", inst);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
@@ -215,16 +199,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
|
||||
} else {
|
||||
ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
@@ -264,17 +238,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
// GLSL only exposes 8 legacy texcoords
|
||||
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
|
||||
TexCoordIndex(attr));
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
@@ -312,33 +275,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("gl_FrontColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontSpecularR:
|
||||
case IR::Attribute::ColorFrontSpecularG:
|
||||
case IR::Attribute::ColorFrontSpecularB:
|
||||
case IR::Attribute::ColorFrontSpecularA:
|
||||
ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackDiffuseR:
|
||||
case IR::Attribute::ColorBackDiffuseG:
|
||||
case IR::Attribute::ColorBackDiffuseB:
|
||||
case IR::Attribute::ColorBackDiffuseA:
|
||||
ctx.Add("gl_BackColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackSpecularR:
|
||||
case IR::Attribute::ColorBackSpecularG:
|
||||
case IR::Attribute::ColorBackSpecularB:
|
||||
case IR::Attribute::ColorBackSpecularA:
|
||||
ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::FogCoordinate:
|
||||
ctx.Add("gl_FogFragCoord={};", value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
|
||||
@@ -211,27 +211,6 @@ std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.stores.Legacy()) {
|
||||
return;
|
||||
}
|
||||
if (ctx.info.stores.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_FrontColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
header += "vec4 gl_FrontSecondaryColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
header += "vec4 gl_BackColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
header += "vec4 gl_BackSecondaryColor;";
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!StoresPerVertexAttributes(ctx.stage)) {
|
||||
return;
|
||||
@@ -250,7 +229,6 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
|
||||
header += "int gl_ViewportIndex;";
|
||||
}
|
||||
SetupLegacyOutPerVertex(ctx, header);
|
||||
header += "};";
|
||||
if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
|
||||
header += "out int gl_ViewportIndex;";
|
||||
@@ -282,21 +260,6 @@ void SetupInPerVertex(EmitContext& ctx, std::string& header) {
|
||||
}
|
||||
header += "}gl_in[gl_MaxPatchVertices];";
|
||||
}
|
||||
|
||||
void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.loads.Legacy()) {
|
||||
return;
|
||||
}
|
||||
header += "in gl_PerFragment{";
|
||||
if (ctx.info.loads.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_Color;";
|
||||
}
|
||||
header += "};";
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
@@ -361,7 +324,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
||||
}
|
||||
SetupOutPerVertex(*this, header);
|
||||
SetupInPerVertex(*this, header);
|
||||
SetupLegacyInPerFragment(*this, header);
|
||||
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
|
||||
|
||||
@@ -44,14 +44,6 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLegacyAttribute(IR::Attribute attribute) {
|
||||
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) ||
|
||||
attribute == IR::Attribute::FogCoordinate ||
|
||||
(attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
|
||||
if (ctx.stage == Stage::TessellationControl) {
|
||||
@@ -83,17 +75,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
|
||||
}
|
||||
}
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
if (attr == IR::Attribute::FogCoordinate) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
|
||||
ctx.Const(0u));
|
||||
} else {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
|
||||
element_id);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PointSize:
|
||||
return ctx.output_point_size;
|
||||
@@ -327,18 +308,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
}
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
if (attr == IR::Attribute::FogCoordinate) {
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
|
||||
ctx.InputLegacyAttribute(attr), ctx.Const(0u))};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
} else {
|
||||
const Id element_id{ctx.Const(element)};
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
|
||||
ctx.InputLegacyAttribute(attr), element_id)};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
enum class Operation {
|
||||
Increment,
|
||||
Decrement,
|
||||
@@ -432,34 +430,6 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
|
||||
return pointer_type;
|
||||
}
|
||||
}
|
||||
|
||||
size_t FindAndSetNextUnusedLocation(std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset) {
|
||||
for (size_t location = start_offset; location < used_locations.size(); ++location) {
|
||||
if (!used_locations.test(location)) {
|
||||
start_offset = location;
|
||||
used_locations.set(location);
|
||||
return location;
|
||||
}
|
||||
}
|
||||
throw RuntimeError("Unable to get an unused location for legacy attribute");
|
||||
}
|
||||
|
||||
Id DefineLegacyInput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset) {
|
||||
const Id id{DefineInput(ctx, ctx.F32[4], true)};
|
||||
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
|
||||
ctx.Decorate(id, spv::Decoration::Location, location);
|
||||
return id;
|
||||
}
|
||||
|
||||
Id DefineLegacyOutput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset, std::optional<u32> invocations) {
|
||||
const Id id{DefineOutput(ctx, ctx.F32[4], invocations)};
|
||||
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
|
||||
ctx.Decorate(id, spv::Decoration::Location, location);
|
||||
return id;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
|
||||
@@ -543,64 +513,6 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
|
||||
return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
|
||||
}
|
||||
|
||||
Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) {
|
||||
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorFrontDiffuseA) {
|
||||
return input_front_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
|
||||
attribute <= IR::Attribute::ColorFrontSpecularA) {
|
||||
return input_front_secondary_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackDiffuseA) {
|
||||
return input_back_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackSpecularR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) {
|
||||
return input_back_secondary_color;
|
||||
}
|
||||
if (attribute == IR::Attribute::FogCoordinate) {
|
||||
return input_fog_frag_coord;
|
||||
}
|
||||
if (attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q) {
|
||||
u32 index =
|
||||
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
return input_fixed_fnc_textures[index];
|
||||
}
|
||||
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
|
||||
}
|
||||
|
||||
Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) {
|
||||
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorFrontDiffuseA) {
|
||||
return output_front_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
|
||||
attribute <= IR::Attribute::ColorFrontSpecularA) {
|
||||
return output_front_secondary_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackDiffuseA) {
|
||||
return output_back_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackSpecularR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) {
|
||||
return output_back_secondary_color;
|
||||
}
|
||||
if (attribute == IR::Attribute::FogCoordinate) {
|
||||
return output_fog_frag_coord;
|
||||
}
|
||||
if (attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q) {
|
||||
u32 index =
|
||||
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
return output_fixed_fnc_textures[index];
|
||||
}
|
||||
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
|
||||
}
|
||||
|
||||
void EmitContext::DefineCommonTypes(const Info& info) {
|
||||
void_id = TypeVoid();
|
||||
|
||||
@@ -1389,7 +1301,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
loads[IR::Attribute::TessellationEvaluationPointV]) {
|
||||
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index)) {
|
||||
@@ -1401,7 +1312,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (input_type == AttributeType::Disabled) {
|
||||
continue;
|
||||
}
|
||||
used_locations.set(index);
|
||||
const Id type{GetAttributeType(*this, input_type)};
|
||||
const Id id{DefineInput(*this, type, true)};
|
||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
||||
@@ -1427,30 +1337,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
input_front_secondary_color =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
input_back_secondary_color =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::FogCoordinate)) {
|
||||
input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
input_fixed_fnc_textures[index] =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
}
|
||||
if (stage == Stage::TessellationEval) {
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
@@ -1501,38 +1387,9 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
|
||||
spv::BuiltIn::ViewportMaskNV);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
DefineGenericOutput(*this, index, invocations);
|
||||
used_locations.set(index);
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
output_front_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
output_front_secondary_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
output_back_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
output_back_secondary_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) {
|
||||
output_fog_frag_coord =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
output_fixed_fnc_textures[index] =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
}
|
||||
switch (stage) {
|
||||
|
||||
@@ -113,9 +113,6 @@ public:
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
Id InputLegacyAttribute(IR::Attribute attribute);
|
||||
Id OutputLegacyAttribute(IR::Attribute attribute);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
@@ -281,22 +278,10 @@ public:
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
Id input_front_color{};
|
||||
Id input_front_secondary_color{};
|
||||
Id input_back_color{};
|
||||
Id input_back_secondary_color{};
|
||||
Id input_fog_frag_coord{};
|
||||
std::array<Id, 10> input_fixed_fnc_textures{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
Id output_front_color{};
|
||||
Id output_front_secondary_color{};
|
||||
Id output_back_color{};
|
||||
Id output_back_secondary_color{};
|
||||
Id output_fog_frag_coord{};
|
||||
std::array<Id, 10> output_fixed_fnc_textures{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
|
||||
@@ -224,6 +224,8 @@ enum class Attribute : u64 {
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
@@ -127,6 +128,42 @@ void AddNVNStorageBuffers(IR::Program& program) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLegacyAttribute(IR::Attribute attribute) {
|
||||
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) ||
|
||||
attribute == IR::Attribute::FogCoordinate ||
|
||||
(attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q);
|
||||
}
|
||||
|
||||
std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||
const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) {
|
||||
std::map<IR::Attribute, IR::Attribute> mapping;
|
||||
for (size_t index = 0; index < 4; ++index) {
|
||||
auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
|
||||
if (state.AnyComponent(attr)) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mapping.insert({attr + i, ununsed_generics.front() + i});
|
||||
}
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
}
|
||||
if (state[IR::Attribute::FogCoordinate]) {
|
||||
mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()});
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
|
||||
if (state.AnyComponent(attr)) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mapping.insert({attr + i, ununsed_generics.front() + i});
|
||||
}
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
@@ -226,4 +263,62 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
|
||||
auto& stores = program.info.stores;
|
||||
if (stores.Legacy()) {
|
||||
std::queue<IR::Attribute> ununsed_output_generics{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!stores.Generic(index)) {
|
||||
ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4);
|
||||
}
|
||||
}
|
||||
auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics);
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::SetAttribute: {
|
||||
const auto attr = inst.Arg(0).Attribute();
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
stores.Set(mappings[attr], true);
|
||||
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& loads = program.info.loads;
|
||||
if (loads.Legacy()) {
|
||||
std::queue<IR::Attribute> ununsed_input_generics{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
|
||||
input_type == AttributeType::Disabled) {
|
||||
ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4);
|
||||
}
|
||||
}
|
||||
auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics);
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::GetAttribute: {
|
||||
const auto attr = inst.Arg(0).Attribute();
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
loads.Set(mappings[attr], true);
|
||||
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
|
||||
@@ -20,4 +21,7 @@ namespace Shader::Maxwell {
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
||||
Environment& env_vertex_b);
|
||||
|
||||
[[nodiscard]] void ConvertLegacyToGeneric(IR::Program& program,
|
||||
const Shader::RuntimeInfo& runtime_info);
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -53,7 +53,8 @@ struct VaryingState {
|
||||
return AnyComponent(IR::Attribute::ColorFrontDiffuseR) ||
|
||||
AnyComponent(IR::Attribute::ColorFrontSpecularR) ||
|
||||
AnyComponent(IR::Attribute::ColorBackDiffuseR) ||
|
||||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture();
|
||||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture() ||
|
||||
mask[static_cast<size_t>(IR::Attribute::FogCoordinate)];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool FixedFunctionTexture() const noexcept {
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace {
|
||||
using Shader::Backend::GLASM::EmitGLASM;
|
||||
using Shader::Backend::GLSL::EmitGLSL;
|
||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||
using Shader::Maxwell::TranslateProgram;
|
||||
using VideoCommon::ComputeEnvironment;
|
||||
@@ -462,12 +463,14 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)};
|
||||
switch (device.GetShaderBackend()) {
|
||||
case Settings::ShaderBackend::GLSL:
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding);
|
||||
break;
|
||||
case Settings::ShaderBackend::GLASM:
|
||||
sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding);
|
||||
break;
|
||||
case Settings::ShaderBackend::SPIRV:
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
|
||||
|
||||
namespace {
|
||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||
using Shader::Maxwell::TranslateProgram;
|
||||
using VideoCommon::ComputeEnvironment;
|
||||
@@ -543,6 +544,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
infos[stage_index] = &program.info;
|
||||
|
||||
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)};
|
||||
device.SaveShader(code);
|
||||
modules[stage_index] = BuildShader(device, code);
|
||||
|
||||
@@ -1137,8 +1137,13 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
|
||||
} while (has_deleted_images);
|
||||
const ImageBase& src_image = slot_images[src_id];
|
||||
const ImageBase& dst_image = slot_images[dst_id];
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
if (GetFormatType(dst_info.format) != GetFormatType(dst_image.info.format) ||
|
||||
GetFormatType(src_info.format) != GetFormatType(src_image.info.format)) {
|
||||
GetFormatType(src_info.format) != GetFormatType(src_image.info.format) ||
|
||||
!VideoCore::Surface::IsViewCompatible(dst_info.format, dst_image.info.format, false,
|
||||
native_bgr) ||
|
||||
!VideoCore::Surface::IsViewCompatible(src_info.format, src_image.info.format, false,
|
||||
native_bgr)) {
|
||||
// Make sure the images match the expected format.
|
||||
do {
|
||||
has_deleted_images = false;
|
||||
|
||||
@@ -475,11 +475,26 @@ void QtSoftwareKeyboardDialog::open() {
|
||||
row = 0;
|
||||
column = 0;
|
||||
|
||||
const auto* const curr_button =
|
||||
keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column];
|
||||
switch (bottom_osk_index) {
|
||||
case BottomOSKIndex::LowerCase:
|
||||
case BottomOSKIndex::UpperCase: {
|
||||
const auto* const curr_button =
|
||||
keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column];
|
||||
|
||||
// This is a workaround for setFocus() randomly not showing focus in the UI
|
||||
QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
|
||||
// This is a workaround for setFocus() randomly not showing focus in the UI
|
||||
QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
|
||||
break;
|
||||
}
|
||||
case BottomOSKIndex::NumberPad: {
|
||||
const auto* const curr_button = numberpad_buttons[row][column];
|
||||
|
||||
// This is a workaround for setFocus() randomly not showing focus in the UI
|
||||
QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
StartInputThread();
|
||||
}
|
||||
|
||||
@@ -776,6 +776,7 @@ void Config::ReadUIGamelistValues() {
|
||||
ReadBasicSetting(UISettings::values.row_1_text_id);
|
||||
ReadBasicSetting(UISettings::values.row_2_text_id);
|
||||
ReadBasicSetting(UISettings::values.cache_game_list);
|
||||
ReadBasicSetting(UISettings::values.favorites_expanded);
|
||||
const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
|
||||
for (int i = 0; i < favorites_size; i++) {
|
||||
qt_config->setArrayIndex(i);
|
||||
@@ -1300,6 +1301,7 @@ void Config::SaveUIGamelistValues() {
|
||||
WriteBasicSetting(UISettings::values.row_1_text_id);
|
||||
WriteBasicSetting(UISettings::values.row_2_text_id);
|
||||
WriteBasicSetting(UISettings::values.cache_game_list);
|
||||
WriteBasicSetting(UISettings::values.favorites_expanded);
|
||||
qt_config->beginWriteArray(QStringLiteral("favorites"));
|
||||
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
|
||||
qt_config->setArrayIndex(i);
|
||||
|
||||
@@ -173,13 +173,17 @@ void GameList::OnItemExpanded(const QModelIndex& item) {
|
||||
const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
|
||||
type == GameListItemType::UserNandDir ||
|
||||
type == GameListItemType::SysNandDir;
|
||||
|
||||
if (!is_dir) {
|
||||
const bool is_fave = type == GameListItemType::Favorites;
|
||||
if (!is_dir && !is_fave) {
|
||||
return;
|
||||
}
|
||||
|
||||
UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded =
|
||||
tree_view->isExpanded(item);
|
||||
const bool is_expanded = tree_view->isExpanded(item);
|
||||
if (is_fave) {
|
||||
UISettings::values.favorites_expanded = is_expanded;
|
||||
return;
|
||||
}
|
||||
const int item_dir_index = item.data(GameListDir::GameDirRole).toInt();
|
||||
UISettings::values.game_dirs[item_dir_index].expanded = is_expanded;
|
||||
}
|
||||
|
||||
// Event in order to filter the gamelist after editing the searchfield
|
||||
@@ -458,10 +462,13 @@ void GameList::DonePopulating(const QStringList& watch_list) {
|
||||
emit ShowList(!IsEmpty());
|
||||
|
||||
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
|
||||
|
||||
// Add favorites row
|
||||
item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
|
||||
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
|
||||
UISettings::values.favorited_ids.size() == 0);
|
||||
tree_view->expand(item_model->invisibleRootItem()->child(0)->index());
|
||||
tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(),
|
||||
UISettings::values.favorites_expanded.GetValue());
|
||||
for (const auto id : UISettings::values.favorited_ids) {
|
||||
AddFavorite(id);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/fs_paths.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -134,6 +135,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
#include "yuzu/discord_impl.h"
|
||||
#endif
|
||||
@@ -259,10 +262,9 @@ GMainWindow::GMainWindow()
|
||||
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
|
||||
#endif
|
||||
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
|
||||
LOG_INFO(Frontend, "Host RAM: {:.2f} GB",
|
||||
Common::GetMemInfo().TotalPhysicalMemory / 1024.0f / 1024 / 1024);
|
||||
LOG_INFO(Frontend, "Host Swap: {:.2f} GB",
|
||||
Common::GetMemInfo().TotalSwapMemory / 1024.0f / 1024 / 1024);
|
||||
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
|
||||
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
|
||||
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
|
||||
UpdateWindowTitle();
|
||||
|
||||
show();
|
||||
@@ -1302,16 +1304,13 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
|
||||
case Core::SystemResultStatus::ErrorVideoCore:
|
||||
QMessageBox::critical(
|
||||
this, tr("An error occurred initializing the video core."),
|
||||
tr("yuzu has encountered an error while running the video core, please see the "
|
||||
"log for more details."
|
||||
tr("yuzu has encountered an error while running the video core. "
|
||||
"This is usually caused by outdated GPU drivers, including integrated ones. "
|
||||
"Please see the log for more details. "
|
||||
"For more information on accessing the log, please see the following page: "
|
||||
"<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
|
||||
"to "
|
||||
"Upload the Log File</a>."
|
||||
"Ensure that you have the latest graphics drivers for your GPU."));
|
||||
|
||||
"<a href='https://yuzu-emu.org/help/reference/log-files/'>"
|
||||
"How to Upload the Log File</a>. "));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (result > Core::SystemResultStatus::ErrorLoader) {
|
||||
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
|
||||
|
||||
@@ -74,7 +74,6 @@ struct Values {
|
||||
QString game_dir_deprecated;
|
||||
bool game_dir_deprecated_deepscan;
|
||||
QVector<UISettings::GameDir> game_dirs;
|
||||
QVector<u64> favorited_ids;
|
||||
QStringList recent_files;
|
||||
QString language;
|
||||
|
||||
@@ -96,6 +95,8 @@ struct Values {
|
||||
Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
|
||||
std::atomic_bool is_game_list_reload_pending{false};
|
||||
Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"};
|
||||
Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"};
|
||||
QVector<u64> favorited_ids;
|
||||
|
||||
bool configuration_applied;
|
||||
bool reset_to_defaults;
|
||||
|
||||
Reference in New Issue
Block a user