Compare commits

..

118 Commits

Author SHA1 Message Date
vonchenplus
4908a07c20 Address format clang 2021-12-18 14:27:07 +08:00
vonchenplus
6ebc972c2b Remove spirv handle legacy related code 2021-12-18 14:08:50 +08:00
vonchenplus
94652e122d Remove glsl handle legacy related code 2021-12-18 14:03:40 +08:00
Feng Chen
e49184e606 Merge branch 'yuzu-emu:master' into convert_legacy 2021-12-18 13:57:14 +08:00
bunnei
77d06d5df0 Merge pull request #7570 from ameerj/favorites-expanded
game_list: Add persistent setting for the favorites row expanded state
2021-12-17 16:09:05 -08:00
bunnei
e242f16986 Merge pull request #7532 from goldenx86/patch-3
Update video core popup
2021-12-15 22:32:27 -08:00
Matías Locatti
333ccf23f8 Suggestions from CrusadingNinja 2021-12-16 02:57:45 -03:00
Matías Locatti
1cdddd17d2 Changed link 2021-12-16 02:40:30 -03:00
bunnei
7cf74abbf5 Merge pull request #7551 from vonchenplus/fix_blit_image_view_mismatching
Fix blit image/view not compatible
2021-12-15 21:39:53 -08:00
bunnei
b3fc36d989 Merge pull request #7588 from Wunkolo/gibibibi-bytes
yuzu/main: Fix host memory byte units. GB to GiB
2021-12-15 12:04:37 -08:00
bunnei
2a24c415c1 Merge pull request #7589 from yuzu-emu/revert-7565-fix-linux-decoding
Revert "video_core/codecs: refactor ffmpeg searching and handling in cmake"
2021-12-15 00:03:20 -08:00
bunnei
2f32133ad5 Revert "video_core/codecs: refactor ffmpeg searching and handling in cmake" 2021-12-15 00:02:53 -08:00
Wunkolo
44b3abdfc0 yuzu/main: Fix host memory byte units. GB to GiB
I have `134850146304` bytes of ram and Yuzu was saying that I had `125.59 GB`
of ram. But `125.59` is actually the amount of gi**bi**bytes I have. In
gi**ga**bytes I would have `134.9`.

Additionally, I changed the `1024 / 1024 / 1024` here into the `_GiB`
user-literals that I added a while ago(#6519).

https://www.wolframalpha.com/input/?i=134850146304+bytes
2021-12-14 23:57:33 -08:00
bunnei
156215d1fa Merge pull request #7565 from liushuyu/fix-linux-decoding
video_core/codecs: refactor ffmpeg searching and handling in cmake
2021-12-14 22:27:13 -08:00
Mai M
019ae82c94 Merge pull request #7558 from Morph1984/unused-cpu-family-model
common/cpu_detect: Remove CPU family and model
2021-12-14 23:43:16 -05:00
Mai M
822259a3f2 Merge pull request #7549 from Morph1984/astc-8x5
maxwell_to_vk: Add ASTC_2D_8X5_UNORM
2021-12-14 23:42:44 -05:00
Mai M
f0ed11e318 Merge pull request #7579 from Morph1984/swkbd-oob-array-access
qt_software_keyboard: Fix out of bounds array access
2021-12-14 23:42:07 -05:00
Ghost
935fee18ed Merge pull request #7583 from german77/triggered
core/hid: Fix faulty analog triggers
2021-12-14 22:48:53 -05:00
Narr the Reg
6aac5d4c27 core/hid: Fix faulty analog triggers 2021-12-14 19:49:44 -06:00
Narr the Reg
5e732e7aec Merge pull request #7581 from lioncash/input-iface
common/input: Avoid numerous large copies of CallbackStatus
2021-12-14 19:10:39 -06:00
Narr the Reg
ac0c5be7c0 Merge pull request #7577 from v1993/patch-2
input/SDL: Update SDL hints
2021-12-14 10:12:17 -06:00
Valeri
7f965172c5 input/SDL: Update SDL hints
SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED is no longer needed thanks to new default in SDL 2.0.18.
SDL_HINT_JOYSTICK_HIDAPI_XBOX is reported to cause conflicts with native driver Xbox driver on Linux, and Xbox controllers don't benefit from hidapi anyways.
2021-12-14 15:02:03 +03:00
liushuyu
dd72e4dce4 CI: fix CI on Linux 2021-12-13 22:31:19 -07:00
liushuyu
a2d73eaa10 video_core/codecs: skip decoders that use hw frames ...
... this would resolve some edge-cases where multiple devices are
present and ffmpeg is unable to auto-supply the hw surfaces
2021-12-13 22:29:19 -07:00
bunnei
9fa1cf3e01 Merge pull request #7580 from lioncash/input-doc
input_poller/input_mapping: Silence several -Wdocumentation warnings
2021-12-13 20:21:18 -08:00
Lioncash
e05d2a70b2 common/input: Avoid numerous large copies of CallbackStatus
CallbackStatus instances aren't the cheapest things to copy around
(relative to everything else), given that they're currently 520 bytes in
size and are currently copied numerous times when callbacks are invoked.

Instead, we can pass the status by const reference to avoid all the
copying.
2021-12-13 21:22:02 -05:00
Morph
4af413623b common/cpu_detect: Remove CPU family and model
We currently do not make use of these fields, remove them for now.
2021-12-13 20:45:18 -05:00
Lioncash
54eafbaf17 common/input: Remove unnecessary returns
Given these return void, these can be omitted.
2021-12-13 20:43:12 -05:00
Lioncash
5e7e38ac72 input_poller: Add missing override specifiers 2021-12-13 20:41:17 -05:00
Lioncash
6497fbfa96 input_mapping: Amend specification of parameters
param tags are supposed to specify the parameter name without any
quoting.

Silences several -Wdocumentation warnings.
2021-12-13 20:31:59 -05:00
Lioncash
7783c0aaef input_poller: Remove several unnecessary @param tags
Silences quite a bit of -Wdocumentation warnings, given the @param tag
is only intended to be used to identify function parameters, not what it
contains.
2021-12-13 20:28:30 -05:00
bunnei
ad45963b45 Merge pull request #7575 from lioncash/input
input_engine: Minor object churn cleanup
2021-12-13 16:50:54 -08:00
liushuyu
37f1c76613 CI: fix MinGW installation step 2021-12-13 17:48:05 -07:00
liushuyu
f91b6fbbcb ffmpeg: move the whole tree into externals/ffmpeg/ffmpeg ...
* this resolves the todo items in the CMakeLists.txt
* a version requirement check for ffmpeg is added to catch issues early
* for future-proof reasons, nasm/yasm is now only required when build on
  x86/AMD64 systems
2021-12-13 17:48:05 -07:00
liushuyu
ccc0a1e621 cmake: refactor ffmpeg searching and handling logic on Linux 2021-12-13 17:48:05 -07:00
Morph
96579e515a qt_software_keyboard: Fix out of bounds array access
We were unconditionally accessing the keyboard_buttons array, even if the bottom_osk_index was for the numberpad, leading to an out of bounds array access. Fix this by accessing the proper array for the current button when the index is for the numberpad.
2021-12-13 19:04:07 -05:00
bunnei
7276aaf907 Merge pull request #7576 from lioncash/tasenum
tas_input: Minor cleanup
2021-12-13 14:47:24 -08:00
Lioncash
54ca48e8b7 tas_input: Avoid minor copies in Read/WriteCommandButtons()
We don't need to copy the whole pair
2021-12-13 11:45:19 -05:00
Lioncash
734fb180bb tas_input: Remove unnecessary semicolon
Resolves a -Wextra-semi warning
2021-12-13 11:45:19 -05:00
Lioncash
ddda6ae776 tas_input: Execute clear() even if empty
clear() when empty is simply a no-op, so we can get rid of the check
here and let the stdlib do it for us.
2021-12-13 11:45:18 -05:00
Lioncash
db9320e754 tas_input: Remove unnecessary includes
Gets rid of indirect includes and includes only what the interface
needs.
2021-12-13 11:45:18 -05:00
Lioncash
26ef76213c tas_input: std::move strings into vector
While we're in the same area, we can also avoid performing std::stoi in
a loop when it only needs to be performed once.
2021-12-13 11:45:15 -05:00
Lioncash
a515ede2af tas_input: Use istringstream over stringstream
This is only using the input facilities, so we don't need to use the
fully-fleged stringstream.
2021-12-13 10:57:53 -05:00
Lioncash
6be730bdcd tas_input: Use u8string_view instead of u8string
Same behavior, but without the potential for extra allocations.
2021-12-13 10:54:43 -05:00
Lioncash
37a8e2a67e tas_input: Remove unused std::smatch variable
This also means we can get rid of the dependency on <regex>
2021-12-13 10:50:24 -05:00
Lioncash
d52ad96ce3 tas_input: Amend -Wdocumentation warnings
Parameters shouldn't have the colon by their name.
2021-12-13 10:49:11 -05:00
Lioncash
c126b0718c tas_input: Make TasAxes enum an enum class
Prevents these values from potentially clashing with anything in other
headers.
2021-12-13 10:41:32 -05:00
Lioncash
e4de1783e1 input_engine: Fix typo in TriggerOnAxisChange() parameter name 2021-12-13 10:21:37 -05:00
Lioncash
a9d39b6895 input_engine: Simplify PreSet* family of functions
We can make use of try_emplace() to insert values only if they don't
already exist.
2021-12-13 10:18:04 -05:00
Lioncash
4d4a234476 input_engine: Avoid redundant map lookups
We can use iterators to avoid looking up into maps twice in the getter
functions.

At the same time we can also avoid copying the ControllerData structs,
since they're 264 bytes in size.
2021-12-13 09:57:51 -05:00
Lioncash
e51b852aee input_engine: Remove left-over namespace qualifiers
These types are part of the InputCommon namespace.
2021-12-13 09:57:51 -05:00
Lioncash
e826e6715a input_engine: Iterate by reference rather than by value where applicable
Avoids creating copies of several object instances (some of which being
over 100 bytes in size).
2021-12-13 09:57:51 -05:00
Lioncash
755822ceec input_engine: Take BasicMotion by const reference with SetMotion() and TriggerOnMotionChange()
Copies the BasicMotion instance once instead of twice.
2021-12-13 09:57:39 -05:00
Lioncash
a92dbec962 input_engine: std::move InputIdentifier in SetCallback()
Allows avoiding std::function allocations.
2021-12-13 09:23:23 -05:00
Lioncash
985599e485 input_engine: Pass LedStatus by const reference
Avoids copies where reasonably applicable
2021-12-13 09:20:58 -05:00
Lioncash
38f3442ea5 input_engine: Pass VibrationStatus by const reference in SetRumble()
Avoids creating copies of the struct where not necessary.
2021-12-13 09:16:10 -05:00
Lioncash
2b92d22bda input_engine: std::move engine name where applicable
We can allow the name to be moved into, allowing allocations to be
avoided.
2021-12-13 09:05:50 -05:00
Lioncash
9a104e2b60 input_engine: Remove callback clearing in constructor
The callback map is a member variable, so this will always be empty on
initial construction.
2021-12-13 08:54:23 -05:00
Lioncash
479369db43 input_engine: Remove unnecessary semi-colons
Silences -Wextra-semi warnings
2021-12-13 08:53:01 -05:00
Lioncash
3c618a3306 input_engine: Remove unnecessary return
This is a void function, so it doesn't need this.
2021-12-13 08:52:09 -05:00
Mai M
3592628302 Merge pull request #7574 from v1993/patch-1
Remove erroneous #pragma once
2021-12-13 08:50:08 -05:00
Valeri
04301e1a8a Remove erroneous #pragma once 2021-12-13 16:49:01 +03:00
Mai M
4e2fb9e51a Merge pull request #7554 from Tachi107/build-remove-unicorn
build: remove remaining bits of Unicorn
2021-12-13 08:24:47 -05:00
bunnei
a2f842ce54 Merge pull request #7527 from Tachi107/cubeb-result_of
build: update cubeb and remove the result_of comment
2021-12-12 22:44:05 -08:00
bunnei
280c779898 Merge pull request #7462 from bunnei/kernel-improve-scheduling
Kernel: Improve threading & scheduling V3
2021-12-12 22:43:25 -08:00
ameerj
b64d28492a game_list: Add persistent setting for the favorites row expanded state
Previously, the favorites row was always expanded on launch. This change introduces a persistent setting that allows the favorites row's expanded state to be remembered between launches.
2021-12-12 15:46:42 -05:00
Andrea Pappacoda
eec9aace60 build: remove remaining bits of Unicorn
Unicorn has been removed in fc6db97a09
2021-12-10 12:25:04 +01:00
Feng Chen
1598426493 Fix blit image/view not compatible 2021-12-10 12:41:09 +08:00
Matías Locatti
60bdedc7dd main: Update video core popup
Old version had formatting issues, and I want to provide an answer to the most common reason this pops up in the first place, outdated drivers.
2021-12-06 20:35:07 -05:00
bunnei
257d3c9ecf hle: kernel k_scheduler: EnableScheduling: Remove redundant GetCurrentThreadPointer calls. 2021-12-06 16:39:18 -08:00
bunnei
9a9e7dd78b hle: kernel k_process: Remove unnecessary .at usage with thread pinning methods. 2021-12-06 16:39:18 -08:00
bunnei
834c25f4d9 hle: kernel: Remove unnecessary virtual specifier on NotifyAvailable. 2021-12-06 16:39:18 -08:00
bunnei
a63af9860b hle: kernel: Remove unnecessary virtual specifier on EndWait. 2021-12-06 16:39:18 -08:00
bunnei
3f8eb44e7d hle: kernel: k_light_condition_variable: Revert unnecessary license comment changes. 2021-12-06 16:39:18 -08:00
bunnei
2e8d737a96 hle: kernel: k_condition_variable: Revert unnecessary style changes. 2021-12-06 16:39:18 -08:00
bunnei
0d1bdfc1d4 hle: kernel: Remove unnecessary virtual specifier on CancelWait. 2021-12-06 16:39:18 -08:00
bunnei
d7f6d516ce hle: kernel: service_thread: Force stop threads on destruction. 2021-12-06 16:39:18 -08:00
bunnei
e596fac6ee hle: kernel: k_light_lock: Implement CancelWait.
- Fixes a crash in Megadimension Neptunia VII.
2021-12-06 16:39:18 -08:00
bunnei
efb5de1c5f hle: kernel: service_thread: Use std::jthread.
- Fixes a potential deadlock on service thread shutdown.
2021-12-06 16:39:18 -08:00
bunnei
a2384a18fa hle: kernel: k_thread: Skip reschedule on DisableDispatch with SC. 2021-12-06 16:39:18 -08:00
bunnei
42697527ba hle: kernel: k_thread: Rename sleeping_queue -> wait_queue. 2021-12-06 16:39:18 -08:00
bunnei
e3d156ab0e hle: kernel: svc: Fix deadlock that can occur with single core. 2021-12-06 16:39:18 -08:00
bunnei
0d9afdedc4 hle: kernel: k_thread: Treat dummy threads as a special type. 2021-12-06 16:39:18 -08:00
FernandoS27
894ed14ebc hle: kernel: fix timing on thread preemption 2021-12-06 16:39:18 -08:00
FernandoS27
3c2a451f47 hle: kernel: fix scheduling ops from HLE host thread. 2021-12-06 16:39:17 -08:00
bunnei
abbea575cf hle: kernel: Add a flag for indicating that the kernel is currently shutting down. 2021-12-06 16:39:17 -08:00
bunnei
2c49a65d2b hle: kernel: KSynchronizationObject: Fix variable shadowing. 2021-12-06 16:39:17 -08:00
bunnei
8f4ff06c4c hle: kernel: Cleanup to match coding style. 2021-12-06 16:39:17 -08:00
bunnei
316a2dd22a hle: kernel: KProcess: Improvements for thread pinning. 2021-12-06 16:39:17 -08:00
bunnei
4c74761155 hle: kernel: KThreadQueue: Remove deprecated code. 2021-12-06 16:39:17 -08:00
bunnei
f3d6e31e78 hle: kernel: KConditionVariable: Various updates & simplifications. 2021-12-06 16:39:17 -08:00
bunnei
f62c7091a2 hle: kernel: KThread: Migrate to updated KThreadQueue (part 2). 2021-12-06 16:39:17 -08:00
bunnei
b0671c7cfa hle: kernel: KThread: Migrate to updated KThreadQueue (part 1). 2021-12-06 16:39:17 -08:00
bunnei
beb55cb90e hle: kernel: KConditionVariable: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
e942d97540 hle: kernel: KServerSession: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
5dff28290f hle: kernel: KLightConditionVariable: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
423acf53b7 hle: kernel: KLightLock: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
15c721b909 hle: kernel: KAddressArbiter: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
2f89456041 hle: kernel: KThread: Remove tracking of sync object from threads. 2021-12-06 16:39:17 -08:00
bunnei
bc1399204b hle: kernel: Update KThreadQueue and migrate KSynchronizationObject. 2021-12-06 16:39:17 -08:00
bunnei
3dc803a430 core: hle: kernel: Disable dispatch count tracking on single core.
- This would have limited value, and would be a mess to handle properly.
2021-12-06 16:39:17 -08:00
bunnei
d14b8fc747 core: hle: kernel: k_thread: Mark KScopedDisableDispatch as nodiscard. 2021-12-06 16:39:17 -08:00
bunnei
08c63d5c75 core: cpu_manager: Use invalid core_id on init and simplify shutdown. 2021-12-06 16:39:17 -08:00
bunnei
07690572f7 core: hle: kernel: k_auto_object: Add GetName method.
- Useful purely for debugging.
2021-12-06 16:39:16 -08:00
bunnei
3239442de6 core: hle: kernel: DisableDispatch on suspend threads. 2021-12-06 16:39:16 -08:00
bunnei
284015dfd7 core: hle: kernel: k_scheduler: Improve DisableScheduling and EnableScheduling. 2021-12-06 16:39:16 -08:00
bunnei
d604edfedf core: cpu_manager: Use KScopedDisableDispatch. 2021-12-06 16:39:16 -08:00
bunnei
178584e56f core: hle: kernel: Use CurrentPhysicalCoreIndex as appropriate. 2021-12-06 16:39:16 -08:00
bunnei
629f9274ac core: hle: kernel: k_scheduler: Remove unnecessary MakeCurrentProcess. 2021-12-06 16:39:16 -08:00
bunnei
13c82d042f core: hle: kernel: k_scheduler: Improve ScheduleImpl. 2021-12-06 16:39:16 -08:00
bunnei
f412d2027a core: hle: kernel: k_scheduler: Improve Unload. 2021-12-06 16:39:16 -08:00
bunnei
f13fce3953 core: hle: kernel: k_process: DisableDispatch on main thread. 2021-12-06 16:39:16 -08:00
bunnei
4c18a207a4 core: hle: kernel: k_handle_table: Use KScopedDisableDispatch as necessary. 2021-12-06 16:39:16 -08:00
bunnei
04daefa488 core: hle: kernel: k_thread: Add KScopedDisableDispatch. 2021-12-06 16:39:16 -08:00
bunnei
3bd5d4b6f8 core: hle: kernel: Ensure idle threads are closed before destroying scheduler. 2021-12-06 16:39:16 -08:00
bunnei
669a2d2c67 core: hle: kernel: Reflect non-emulated threads as core 3. 2021-12-06 16:39:16 -08:00
Andrea Pappacoda
f4587c596f build: update cubeb and remove the result_of comment
Cubeb doesn't use result_of anymore, it has been dropped in commit
mozilla/cubeb@75d9d125ee
2021-12-06 18:56:43 +01:00
Feng Chen
4dd85f86a8 Implement convert legacy to generic 2021-11-19 22:53:58 +08:00
83 changed files with 1566 additions and 1451 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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++;
}

View File

@@ -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

View File

@@ -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++;
}

View File

@@ -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

View File

@@ -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

View File

@@ -170,6 +170,10 @@ public:
}
}
const std::string& GetName() const {
return name;
}
private:
void RegisterWithKernel();
void UnregisterWithKernel();

View File

@@ -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

View File

@@ -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;

View File

@@ -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.

View File

@@ -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.

View 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

View File

@@ -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{};
};

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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.

View File

@@ -347,6 +347,7 @@ public:
void PinCurrentThread();
void UnpinCurrentThread();
void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {
return state_lock;

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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"

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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();
}
});
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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.

View File

@@ -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

View File

@@ -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);

View File

@@ -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.

View File

@@ -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{};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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};

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -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),

View File

@@ -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++;
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 "";

View File

@@ -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:

View File

@@ -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)) {

View File

@@ -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));

View File

@@ -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) {

View File

@@ -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{};

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;