summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/audio_core/delay_line.cpp4
-rw-r--r--src/audio_core/delay_line.h4
-rw-r--r--src/common/host_memory.cpp4
-rw-r--r--src/common/input.h18
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/x64/cpu_detect.cpp12
-rw-r--r--src/common/x64/native_clock.cpp36
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/core.cpp6
-rw-r--r--src/core/core.h3
-rw-r--r--src/core/cpu_manager.cpp23
-rw-r--r--src/core/hid/emulated_console.cpp21
-rw-r--r--src/core/hid/emulated_console.h32
-rw-r--r--src/core/hid/emulated_controller.cpp144
-rw-r--r--src/core/hid/emulated_controller.h85
-rw-r--r--src/core/hid/emulated_devices.cpp66
-rw-r--r--src/core/hid/emulated_devices.h49
-rw-r--r--src/core/hid/hid_core.cpp46
-rw-r--r--src/core/hid/hid_core.h11
-rw-r--r--src/core/hid/hid_types.h4
-rw-r--r--src/core/hid/input_converter.h23
-rw-r--r--src/core/hid/input_interpreter.cpp16
-rw-r--r--src/core/hid/input_interpreter.h2
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp92
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_class_token.cpp5
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp146
-rw-r--r--src/core/hle/kernel/k_code_memory.h66
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp245
-rw-r--r--src/core/hle/kernel/k_condition_variable.h2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h2
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.cpp80
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.h58
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp72
-rw-r--r--src/core/hle/kernel/k_light_lock.h2
-rw-r--r--src/core/hle/kernel/k_memory_block.h20
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp119
-rw-r--r--src/core/hle/kernel/k_page_table.h8
-rw-r--r--src/core/hle/kernel/k_process.cpp28
-rw-r--r--src/core/hle/kernel/k_process.h1
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp101
-rw-r--r--src/core/hle/kernel/k_scheduler.h2
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h10
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h1
-rw-r--r--src/core/hle/kernel/k_server_session.cpp3
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp151
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h32
-rw-r--r--src/core/hle/kernel/k_thread.cpp246
-rw-r--r--src/core/hle/kernel/k_thread.h72
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp49
-rw-r--r--src/core/hle/kernel/k_thread_queue.h74
-rw-r--r--src/core/hle/kernel/kernel.cpp81
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/service_thread.cpp33
-rw-r--r--src/core/hle/kernel/svc.cpp249
-rw-r--r--src/core/hle/kernel/svc_wrap.h27
-rw-r--r--src/core/hle/kernel/time_manager.cpp6
-rw-r--r--src/core/hle/service/am/am.cpp14
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/hwopus.cpp4
-rw-r--r--src/core/hle/service/caps/caps.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp23
-rw-r--r--src/core/hle/service/glue/glue.cpp4
-rw-r--r--src/core/hle/service/glue/notif.cpp44
-rw-r--r--src/core/hle/service/glue/notif.h25
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp203
-rw-r--r--src/core/hle/service/hid/controllers/npad.h12
-rw-r--r--src/core/hle/service/hid/hid.cpp61
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h4
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp5
-rw-r--r--src/core/memory/cheat_engine.cpp3
-rw-r--r--src/core/perf_stats.h4
-rw-r--r--src/input_common/drivers/gc_adapter.cpp6
-rw-r--r--src/input_common/drivers/gc_adapter.h8
-rw-r--r--src/input_common/drivers/keyboard.cpp2
-rw-r--r--src/input_common/drivers/keyboard.h4
-rw-r--r--src/input_common/drivers/mouse.cpp2
-rw-r--r--src/input_common/drivers/mouse.h4
-rw-r--r--src/input_common/drivers/sdl_driver.cpp22
-rw-r--r--src/input_common/drivers/sdl_driver.h14
-rw-r--r--src/input_common/drivers/tas_input.cpp95
-rw-r--r--src/input_common/drivers/tas_input.h65
-rw-r--r--src/input_common/drivers/touch_screen.cpp2
-rw-r--r--src/input_common/drivers/touch_screen.h4
-rw-r--r--src/input_common/drivers/udp_client.cpp2
-rw-r--r--src/input_common/drivers/udp_client.h6
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp75
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp11
-rw-r--r--src/input_common/input_engine.cpp86
-rw-r--r--src/input_common/input_engine.h53
-rw-r--r--src/input_common/input_mapping.h28
-rw-r--r--src/input_common/input_poller.cpp39
-rw-r--r--src/input_common/input_poller.h212
-rw-r--r--src/shader_recompiler/CMakeLists.txt12
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp22
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp31
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp18
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp26
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp171
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_select.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_special.cpp95
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp30
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.cpp (renamed from src/shader_recompiler/backend/glasm/emit_context.cpp)2
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.h (renamed from src/shader_recompiler/backend/glasm/emit_context.h)0
-rw-r--r--src/shader_recompiler/backend/glasm/reg_alloc.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_select.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp (renamed from src/shader_recompiler/backend/glsl/emit_context.cpp)2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.h (renamed from src/shader_recompiler/backend/glsl/emit_context.h)0
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_select.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_special.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp (renamed from src/shader_recompiler/backend/spirv/emit_context.cpp)2
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h (renamed from src/shader_recompiler/backend/spirv/emit_context.h)0
-rw-r--r--src/shader_recompiler/environment.h4
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp115
-rw-r--r--src/video_core/gpu.cpp44
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp50
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp123
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h30
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h8
-rw-r--r--src/video_core/shader_notify.cpp2
-rw-r--r--src/video_core/shader_notify.h2
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp2
-rw-r--r--src/video_core/texture_cache/texture_cache.h29
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp1
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/applets/qt_controller.cpp10
-rw-r--r--src/yuzu/applets/qt_controller.h4
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp17
-rw-r--r--src/yuzu/applets/qt_profile_select.h8
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp23
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp163
-rw-r--r--src/yuzu/debugger/profiler.cpp2
-rw-r--r--src/yuzu/game_list.cpp39
-rw-r--r--src/yuzu/game_list.h5
-rw-r--r--src/yuzu/loading_screen.cpp4
-rw-r--r--src/yuzu/loading_screen.h4
-rw-r--r--src/yuzu/main.cpp52
-rw-r--r--src/yuzu/uisettings.h3
-rw-r--r--src/yuzu/util/controller_navigation.cpp177
-rw-r--r--src/yuzu/util/controller_navigation.h51
205 files changed, 3567 insertions, 1904 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 63dd9febf..19d16147d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -24,6 +24,7 @@ if (MSVC)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
+ # /Zm - Specifies the precompiled header memory allocation limit
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
@@ -36,6 +37,7 @@ if (MSVC)
add_compile_options(
/MP
/Zi
+ /Zm200
/Zo
/permissive-
/EHsc
diff --git a/src/audio_core/delay_line.cpp b/src/audio_core/delay_line.cpp
index f4e4dd8d2..2793ed8db 100644
--- a/src/audio_core/delay_line.cpp
+++ b/src/audio_core/delay_line.cpp
@@ -1,3 +1,7 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
#include <cstring>
#include "audio_core/delay_line.h"
diff --git a/src/audio_core/delay_line.h b/src/audio_core/delay_line.h
index cafddd432..84f11bc52 100644
--- a/src/audio_core/delay_line.h
+++ b/src/audio_core/delay_line.h
@@ -1,3 +1,7 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
#pragma once
#include "common/common_types.h"
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index b44a44949..28949fe5e 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -1,3 +1,7 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
#ifdef _WIN32
#include <iterator>
diff --git a/src/common/input.h b/src/common/input.h
index eaee0bdea..f775a4c01 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -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;
}
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 42744c994..b898a652c 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -114,6 +114,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NGCT) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
+ SUB(Service, NOTIF) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 2d21fc483..9ed0c7ad6 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -82,6 +82,7 @@ enum class Class : u8 {
Service_NGCT, ///< The NGCT (No Good Content for Terra) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
+ Service_NOTIF, ///< The NOTIF (Notification) service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index fccd2eee5..fbeacc7e2 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -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)
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 87de40624..82ee2c8a1 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -15,26 +15,26 @@
namespace Common {
u64 EstimateRDTSCFrequency() {
- const auto milli_10 = std::chrono::milliseconds{10};
- // get current time
+ // Discard the first result measuring the rdtsc.
_mm_mfence();
- const u64 tscStart = __rdtsc();
- const auto startTime = std::chrono::high_resolution_clock::now();
- // wait roughly 3 seconds
- while (true) {
- auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::high_resolution_clock::now() - startTime);
- if (milli.count() >= 3000)
- break;
- std::this_thread::sleep_for(milli_10);
- }
- const auto endTime = std::chrono::high_resolution_clock::now();
+ __rdtsc();
+ std::this_thread::sleep_for(std::chrono::milliseconds{1});
+ _mm_mfence();
+ __rdtsc();
+
+ // Get the current time.
+ const auto start_time = std::chrono::steady_clock::now();
+ _mm_mfence();
+ const u64 tsc_start = __rdtsc();
+ // Wait for 200 milliseconds.
+ std::this_thread::sleep_for(std::chrono::milliseconds{200});
+ const auto end_time = std::chrono::steady_clock::now();
_mm_mfence();
- const u64 tscEnd = __rdtsc();
- // calculate difference
- const u64 timer_diff =
- std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
- const u64 tsc_diff = tscEnd - tscStart;
+ const u64 tsc_end = __rdtsc();
+ // Calculate differences.
+ const u64 timer_diff = static_cast<u64>(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
+ const u64 tsc_diff = tsc_end - tsc_start;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
return tsc_freq;
}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eee8e2ccd..49bed614a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -179,12 +179,15 @@ add_library(core STATIC
hle/kernel/k_client_port.h
hle/kernel/k_client_session.cpp
hle/kernel/k_client_session.h
+ hle/kernel/k_code_memory.cpp
+ hle/kernel/k_code_memory.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
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
@@ -237,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
@@ -408,6 +412,8 @@ add_library(core STATIC
hle/service/glue/glue.h
hle/service/glue/glue_manager.cpp
hle/service/glue/glue_manager.h
+ hle/service/glue/notif.cpp
+ hle/service/glue/notif.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 473ab9f81..aa96f709b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -521,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
return impl->kernel.CurrentPhysicalCore().ArmInterface();
}
-std::size_t System::CurrentCoreIndex() const {
- std::size_t core = impl->kernel.GetCurrentHostThreadID();
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- return core;
-}
-
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
return impl->kernel.CurrentPhysicalCore();
}
diff --git a/src/core/core.h b/src/core/core.h
index 645e5c241..52ff90359 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -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();
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 5d43c6e5d..cbcc54891 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -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();
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index 80db8e9c6..685ec080c 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -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;
}
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 25c183eee..3afd284d5 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -78,9 +78,9 @@ struct ConsoleUpdateCallback {
class EmulatedConsole {
public:
/**
- * Contains all input data related to the console like motion and touch input
+ * Contains all input data within the emulated switch console tablet such as touch and motion
*/
- EmulatedConsole();
+ explicit EmulatedConsole();
~EmulatedConsole();
YUZU_NON_COPYABLE(EmulatedConsole);
@@ -89,14 +89,16 @@ public:
/// Removes all callbacks created from input devices
void UnloadInput();
- /// Sets the emulated console into configuring mode. Locking all HID service events from being
- /// moddified
+ /**
+ * Sets the emulated console into configuring mode
+ * This prevents the modification of the HID state of the emulated console by input commands
+ */
void EnableConfiguration();
- /// Returns the emulated console to the normal behaivour
+ /// Returns the emulated console into normal mode, allowing the modification of the HID state
void DisableConfiguration();
- /// Returns true if the emulated console is on configuring mode
+ /// Returns true if the emulated console is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
@@ -116,7 +118,7 @@ public:
/**
* Updates the current mapped motion device
- * @param ParamPackage with controller data to be mapped
+ * @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(Common::ParamPackage param);
@@ -134,14 +136,14 @@ public:
/**
* Adds a callback to the list of events
- * @param ConsoleUpdateCallback that will be triggered
+ * @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ConsoleUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
- * @param Key corresponding to the callback index in the list
+ * @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
@@ -151,20 +153,20 @@ private:
/**
* Updates the motion status of the console
- * @param A CallbackStatus containing gyro and accelerometer data
+ * @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
+ * @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
- * @param Input type of the event to trigger
+ * @param type Input type of the event to trigger
*/
void TriggerOnChange(ConsoleTriggerType type);
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 466ff5542..93372445b 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -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;
}
@@ -866,7 +879,52 @@ void EmulatedController::SetLedPattern() {
}
}
+void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
+ supported_style_tag = supported_styles;
+ if (!is_connected) {
+ return;
+ }
+ if (!IsControllerSupported()) {
+ LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
+ npad_type);
+ Disconnect();
+ }
+}
+
+bool EmulatedController::IsControllerSupported() const {
+ switch (npad_type) {
+ case NpadStyleIndex::ProController:
+ return supported_style_tag.fullkey;
+ case NpadStyleIndex::Handheld:
+ return supported_style_tag.handheld;
+ case NpadStyleIndex::JoyconDual:
+ return supported_style_tag.joycon_dual;
+ case NpadStyleIndex::JoyconLeft:
+ return supported_style_tag.joycon_left;
+ case NpadStyleIndex::JoyconRight:
+ return supported_style_tag.joycon_right;
+ case NpadStyleIndex::GameCube:
+ return supported_style_tag.gamecube;
+ case NpadStyleIndex::Pokeball:
+ return supported_style_tag.palma;
+ case NpadStyleIndex::NES:
+ return supported_style_tag.lark;
+ case NpadStyleIndex::SNES:
+ return supported_style_tag.lucia;
+ case NpadStyleIndex::N64:
+ return supported_style_tag.lagoon;
+ case NpadStyleIndex::SegaGenesis:
+ return supported_style_tag.lager;
+ default:
+ return false;
+ }
+}
+
void EmulatedController::Connect() {
+ if (!IsControllerSupported()) {
+ LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
+ return;
+ }
{
std::lock_guard lock{mutex};
if (is_configuring) {
@@ -1065,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++;
}
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 2c5d51bc8..e42aafebc 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -132,8 +132,8 @@ struct ControllerUpdateCallback {
class EmulatedController {
public:
/**
- * Contains all input data related to this controller. Like buttons, joysticks, motion.
- * @param Npad id type for this specific controller
+ * Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
+ * @param npad_id_type npad id type for this specific controller
*/
explicit EmulatedController(NpadIdType npad_id_type_);
~EmulatedController();
@@ -155,11 +155,18 @@ public:
/**
* Gets the NpadStyleIndex for this controller
- * @param If true tmp_npad_type will be returned
+ * @param get_temporary_value If true tmp_npad_type will be returned
* @return NpadStyleIndex set on the controller
*/
NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
+ /**
+ * Sets the supported controller types. Disconnects the controller if current type is not
+ * supported
+ * @param supported_styles bitflag with supported types
+ */
+ void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
+
/// Sets the connected status to true
void Connect();
@@ -168,7 +175,7 @@ public:
/**
* Is the emulated connected
- * @param If true tmp_is_connected will be returned
+ * @param get_temporary_value If true tmp_is_connected will be returned
* @return true if the controller has the connected status
*/
bool IsConnected(bool get_temporary_value = false) const;
@@ -179,14 +186,16 @@ public:
/// Removes all callbacks created from input devices
void UnloadInput();
- /// Sets the emulated console into configuring mode. Locking all HID service events from being
- /// moddified
+ /**
+ * Sets the emulated controller into configuring mode
+ * This prevents the modification of the HID state of the emulated controller by input commands
+ */
void EnableConfiguration();
- /// Returns the emulated console to the normal behaivour
+ /// Returns the emulated controller into normal mode, allowing the modification of the HID state
void DisableConfiguration();
- /// Returns true if the emulated device is on configuring mode
+ /// Returns true if the emulated controller is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
@@ -215,19 +224,19 @@ public:
/**
* Updates the current mapped button device
- * @param ParamPackage with controller data to be mapped
+ * @param param ParamPackage with controller data to be mapped
*/
void SetButtonParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped stick device
- * @param ParamPackage with controller data to be mapped
+ * @param param ParamPackage with controller data to be mapped
*/
void SetStickParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped motion device
- * @param ParamPackage with controller data to be mapped
+ * @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
@@ -270,13 +279,13 @@ public:
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
- /*
+ /**
* Sends a specific vibration to the output device
* @return returns true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
- /*
+ /**
* Sends a small vibration to the output device
* @return returns true if SetVibration was successfull
*/
@@ -290,14 +299,14 @@ public:
/**
* Adds a callback to the list of events
- * @param ConsoleUpdateCallback that will be triggered
+ * @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ControllerUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
- * @param Key corresponding to the callback index in the list
+ * @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
@@ -309,55 +318,65 @@ private:
void LoadTASParams();
/**
+ * Checks the current controller type against the supported_style_tag
+ * @return true if the controller is supported
+ */
+ bool IsControllerSupported() const;
+
+ /**
* Updates the button status of the controller
- * @param callback: A CallbackStatus containing the button status
- * @param index: Button ID of the to be updated
+ * @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
+ * @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
+ * @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
+ * @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
+ * @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
- * @param type: Input type of the event to trigger
- * @param is_service_update: indicates if this event should be sended to only services
+ * @param type Input type of the event to trigger
+ * @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
+ NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
f32 motion_sensitivity{0.01f};
bool force_update_motion{false};
- // Temporary values to avoid doing changes while the controller is on configuration mode
+ // Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
bool tmp_is_connected{false};
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 874780ec2..708480f2d 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -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++;
}
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 05a945d08..790d3b411 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -75,9 +75,9 @@ class EmulatedDevices {
public:
/**
* Contains all input data related to external devices that aren't necesarily a controller
- * like keyboard and mouse
+ * This includes devices such as the keyboard or mouse
*/
- EmulatedDevices();
+ explicit EmulatedDevices();
~EmulatedDevices();
YUZU_NON_COPYABLE(EmulatedDevices);
@@ -86,14 +86,16 @@ public:
/// Removes all callbacks created from input devices
void UnloadInput();
- /// Sets the emulated console into configuring mode. Locking all HID service events from being
- /// moddified
+ /**
+ * Sets the emulated devices into configuring mode
+ * This prevents the modification of the HID state of the emulated devices by input commands
+ */
void EnableConfiguration();
- /// Returns the emulated console to the normal behaivour
+ /// Returns the emulated devices into normal mode, allowing the modification of the HID state
void DisableConfiguration();
- /// Returns true if the emulated device is on configuring mode
+ /// Returns true if the emulated device is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
@@ -134,14 +136,14 @@ public:
/**
* Adds a callback to the list of events
- * @param InterfaceUpdateCallback that will be triggered
+ * @param update_callback InterfaceUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(InterfaceUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
- * @param Key corresponding to the callback index in the list
+ * @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
@@ -151,42 +153,41 @@ private:
/**
* Updates the touch status of the keyboard device
- * @param callback: A CallbackStatus containing the key status
- * @param index: key ID to be updated
+ * @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
+ * @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
+ * @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
+ * @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
+ * @param callback A CallbackStatus containing the position status
*/
- void SetMouseStick(Common::Input::CallbackStatus callback);
+ void SetMouseStick(const Common::Input::CallbackStatus& callback);
/**
* Triggers a callback that something has changed on the device status
- * @param Input type of the event to trigger
+ * @param type Input type of the event to trigger
*/
void TriggerOnChange(DeviceTriggerType type);
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index 741a69c3c..a1c3bbb57 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -108,6 +108,16 @@ const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t inde
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
supported_style_tag.raw = style_tag.raw;
+ player_1->SetSupportedNpadStyleTag(supported_style_tag);
+ player_2->SetSupportedNpadStyleTag(supported_style_tag);
+ player_3->SetSupportedNpadStyleTag(supported_style_tag);
+ player_4->SetSupportedNpadStyleTag(supported_style_tag);
+ player_5->SetSupportedNpadStyleTag(supported_style_tag);
+ player_6->SetSupportedNpadStyleTag(supported_style_tag);
+ player_7->SetSupportedNpadStyleTag(supported_style_tag);
+ player_8->SetSupportedNpadStyleTag(supported_style_tag);
+ other->SetSupportedNpadStyleTag(supported_style_tag);
+ handheld->SetSupportedNpadStyleTag(supported_style_tag);
}
NpadStyleTag HIDCore::GetSupportedStyleTag() const {
@@ -135,6 +145,42 @@ NpadIdType HIDCore::GetFirstNpadId() const {
return NpadIdType::Player1;
}
+NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
+ for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
+ const auto* const controller = GetEmulatedControllerByIndex(player_index);
+ if (!controller->IsConnected()) {
+ return controller->GetNpadIdType();
+ }
+ }
+ return NpadIdType::Player1;
+}
+
+void HIDCore::EnableAllControllerConfiguration() {
+ player_1->EnableConfiguration();
+ player_2->EnableConfiguration();
+ player_3->EnableConfiguration();
+ player_4->EnableConfiguration();
+ player_5->EnableConfiguration();
+ player_6->EnableConfiguration();
+ player_7->EnableConfiguration();
+ player_8->EnableConfiguration();
+ other->EnableConfiguration();
+ handheld->EnableConfiguration();
+}
+
+void HIDCore::DisableAllControllerConfiguration() {
+ player_1->DisableConfiguration();
+ player_2->DisableConfiguration();
+ player_3->DisableConfiguration();
+ player_4->DisableConfiguration();
+ player_5->DisableConfiguration();
+ player_6->DisableConfiguration();
+ player_7->DisableConfiguration();
+ player_8->DisableConfiguration();
+ other->DisableConfiguration();
+ handheld->DisableConfiguration();
+}
+
void HIDCore::ReloadInputDevices() {
player_1->ReloadFromSettings();
player_2->ReloadFromSettings();
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index 609f40f3b..837f7de49 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -45,6 +45,15 @@ public:
/// Returns the first connected npad id
NpadIdType GetFirstNpadId() const;
+ /// Returns the first disconnected npad id
+ NpadIdType GetFirstDisconnectedNpadId() const;
+
+ /// Sets all emulated controllers into configuring mode.
+ void EnableAllControllerConfiguration();
+
+ /// Sets all emulated controllers into normal mode.
+ void DisableAllControllerConfiguration();
+
/// Reloads all input devices from settings
void ReloadInputDevices();
@@ -67,7 +76,7 @@ private:
std::unique_ptr<EmulatedController> handheld;
std::unique_ptr<EmulatedConsole> console;
std::unique_ptr<EmulatedDevices> devices;
- NpadStyleTag supported_style_tag;
+ NpadStyleTag supported_style_tag{NpadStyleSet::All};
};
} // namespace Core::HID
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index acf54e233..7c12f01fc 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -63,6 +63,8 @@ enum class NpadButton : u64 {
LagonCUp = 1ULL << 32,
LagonCRight = 1ULL << 33,
LagonCDown = 1ULL << 34,
+
+ All = 0xFFFFFFFFFFFFFFFFULL,
};
DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
@@ -254,6 +256,8 @@ enum class NpadStyleSet : u32 {
Lager = 1U << 11,
SystemExt = 1U << 29,
System = 1U << 30,
+
+ All = 0xFFFFFFFFU,
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index 1492489d7..d24582226 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -21,7 +21,7 @@ namespace Core::HID {
/**
* Converts raw input data into a valid battery status.
*
- * @param Supported callbacks: Analog, Battery, Trigger.
+ * @param callback Supported callbacks: Analog, Battery, Trigger.
* @return A valid BatteryStatus object.
*/
Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
@@ -29,7 +29,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
/**
* Converts raw input data into a valid button status. Applies invert properties to the output.
*
- * @param Supported callbacks: Analog, Button, Trigger.
+ * @param callback Supported callbacks: Analog, Button, Trigger.
* @return A valid TouchStatus object.
*/
Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
@@ -37,7 +37,7 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
/**
* Converts raw input data into a valid motion status.
*
- * @param Supported callbacks: Motion.
+ * @param callback Supported callbacks: Motion.
* @return A valid TouchStatus object.
*/
Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
@@ -46,7 +46,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
* Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
* properties to the output.
*
- * @param Supported callbacks: Stick.
+ * @param callback Supported callbacks: Stick.
* @return A valid StickStatus object.
*/
Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
@@ -54,7 +54,7 @@ Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus&
/**
* Converts raw input data into a valid touch status.
*
- * @param Supported callbacks: Touch.
+ * @param callback Supported callbacks: Touch.
* @return A valid TouchStatus object.
*/
Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
@@ -63,7 +63,7 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
* Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
* invert properties to the output. Button status uses the threshold property if necessary.
*
- * @param Supported callbacks: Analog, Button, Trigger.
+ * @param callback Supported callbacks: Analog, Button, Trigger.
* @return A valid TriggerStatus object.
*/
Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
@@ -72,22 +72,23 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
* Converts raw input data into a valid analog status. Applies offset, deadzone, range and
* invert properties to the output.
*
- * @param Supported callbacks: Analog.
+ * @param callback Supported callbacks: Analog.
* @return A valid AnalogStatus object.
*/
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
* Converts raw analog data into a valid analog value
- * @param An analog object containing raw data and properties, bool that determines if the value
- * needs to be clamped between -1.0f and 1.0f.
+ * @param analog An analog object containing raw data and properties
+ * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
*/
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
/**
* Converts raw stick data into a valid stick value
- * @param Two analog objects containing raw data and properties, bool that determines if the value
- * needs to be clamped into the unit circle.
+ * @param analog_x raw analog data and properties for the x-axis
+ * @param analog_y raw analog data and properties for the y-axis
+ * @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
*/
void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
bool clamp_value);
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 870422d82..2dbda8814 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -20,7 +20,7 @@ InputInterpreter::InputInterpreter(Core::System& system)
InputInterpreter::~InputInterpreter() = default;
void InputInterpreter::PollInput() {
- const u64 button_state = npad.GetAndResetPressState();
+ const auto button_state = npad.GetAndResetPressState();
previous_index = current_index;
current_index = (current_index + 1) % button_states.size();
@@ -32,30 +32,30 @@ void InputInterpreter::ResetButtonStates() {
previous_index = 0;
current_index = 0;
- button_states[0] = 0xFFFFFFFFFFFFFFFF;
+ button_states[0] = Core::HID::NpadButton::All;
for (std::size_t i = 1; i < button_states.size(); ++i) {
- button_states[i] = 0;
+ button_states[i] = Core::HID::NpadButton::None;
}
}
bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
- return (button_states[current_index] & static_cast<u64>(button)) != 0;
+ return True(button_states[current_index] & button);
}
bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
- const bool current_press = (button_states[current_index] & static_cast<u64>(button)) != 0;
- const bool previous_press = (button_states[previous_index] & static_cast<u64>(button)) != 0;
+ const bool current_press = True(button_states[current_index] & button);
+ const bool previous_press = True(button_states[previous_index] & button);
return current_press && !previous_press;
}
bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
- u64 held_buttons{button_states[0]};
+ Core::HID::NpadButton held_buttons{button_states[0]};
for (std::size_t i = 1; i < button_states.size(); ++i) {
held_buttons &= button_states[i];
}
- return (held_buttons & static_cast<u64>(button)) != 0;
+ return True(held_buttons & button);
}
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h
index 1c2e02142..70c34d474 100644
--- a/src/core/hid/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -105,7 +105,7 @@ private:
Service::HID::Controller_NPad& npad;
/// Stores 9 consecutive button states polled from HID.
- std::array<u64, 9> button_states{};
+ std::array<Core::HID::NpadButton, 9> button_states{};
std::size_t previous_index{};
std::size_t current_index{};
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 8ff0f695d..36fc0944a 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -9,6 +9,7 @@
#include "core/core.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
@@ -32,6 +33,7 @@ namespace Kernel::Init {
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
+ HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1b429bc1e..783c69858 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -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);
- 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();
- }
+ // Wait for the thread to finish.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// 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);
- 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();
- }
+ // Wait for the thread to finish.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
- KSynchronizationObject* dummy{};
- return cur_thread->GetWaitResult(&dummy);
+ return cur_thread->GetWaitResult();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index e4fcdbc67..165b76747 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -170,6 +170,10 @@ public:
}
}
+ const std::string& GetName() const {
+ return name;
+ }
+
private:
void RegisterWithKernel();
void UnregisterWithKernel();
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index 0be0027be..21e2fe494 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -6,6 +6,7 @@
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
-// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
+static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
// Ensure that the token hierarchy is correct.
@@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
-// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
new file mode 100644
index 000000000..d69f7ffb7
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -0,0 +1,146 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_code_memory.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+KCodeMemory::KCodeMemory(KernelCore& kernel_)
+ : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
+
+ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
+ // Set members.
+ m_owner = kernel.CurrentProcess();
+
+ // Get the owner page table.
+ auto& page_table = m_owner->PageTable();
+
+ // Construct the page group.
+ KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
+ m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+
+ // Lock the memory.
+ R_TRY(page_table.LockForCodeMemory(addr, size))
+
+ // Clear the memory.
+ for (const auto& block : m_page_group.Nodes()) {
+ std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
+ }
+
+ // Set remaining tracking members.
+ m_address = addr;
+ m_is_initialized = true;
+ m_is_owner_mapped = false;
+ m_is_mapped = false;
+
+ // We succeeded.
+ return ResultSuccess;
+}
+
+void KCodeMemory::Finalize() {
+ // Unlock.
+ if (!m_is_mapped && !m_is_owner_mapped) {
+ const size_t size = m_page_group.GetNumPages() * PageSize;
+ m_owner->PageTable().UnlockForCodeMemory(m_address, size);
+ }
+}
+
+ResultCode KCodeMemory::Map(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_mapped, ResultInvalidState);
+
+ // Map the memory.
+ R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
+ address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
+
+ // Mark ourselves as mapped.
+ m_is_mapped = true;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
+ KMemoryState::CodeOut));
+
+ // Mark ourselves as unmapped.
+ m_is_mapped = false;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
+
+ // Convert the memory permission.
+ KMemoryPermission k_perm{};
+ switch (perm) {
+ case Svc::MemoryPermission::Read:
+ k_perm = KMemoryPermission::UserRead;
+ break;
+ case Svc::MemoryPermission::ReadExecute:
+ k_perm = KMemoryPermission::UserReadExecute;
+ break;
+ default:
+ break;
+ }
+
+ // Map the memory.
+ R_TRY(
+ m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
+
+ // Mark ourselves as mapped.
+ m_is_owner_mapped = true;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
+
+ // Mark ourselves as unmapped.
+ m_is_owner_mapped = false;
+
+ return ResultSuccess;
+}
+
+} // namespace Kernel \ No newline at end of file
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
new file mode 100644
index 000000000..e0ba19a53
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -0,0 +1,66 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+enum class CodeMemoryOperation : u32 {
+ Map = 0,
+ MapToOwner = 1,
+ Unmap = 2,
+ UnmapFromOwner = 3,
+};
+
+class KCodeMemory final
+ : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
+ KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
+
+public:
+ explicit KCodeMemory(KernelCore& kernel_);
+
+ ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
+ void Finalize();
+
+ ResultCode Map(VAddr address, size_t size);
+ ResultCode Unmap(VAddr address, size_t size);
+ ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
+ ResultCode UnmapFromOwner(VAddr address, size_t size);
+
+ bool IsInitialized() const {
+ return m_is_initialized;
+ }
+ static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+ KProcess* GetOwner() const {
+ return m_owner;
+ }
+ VAddr GetSourceAddress() const {
+ return m_address;
+ }
+ size_t GetSize() const {
+ return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
+ }
+
+private:
+ KPageLinkedList m_page_group{};
+ KProcess* m_owner{};
+ VAddr m_address{};
+ KLightLock m_lock;
+ bool m_is_initialized{};
+ bool m_is_owner_mapped{};
+ bool m_is_mapped{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 7fa9b8cc3..aadcc297a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -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.
+ KThread* owner_thread{};
{
- KScopedAutoObject<KThread> owner_thread;
- ASSERT(owner_thread.IsNull());
- {
- KScopedSchedulerLock sl(kernel);
- cur_thread->SetSyncedObject(nullptr, ResultSuccess);
+ KScopedSchedulerLock sl(kernel);
- // Check if the thread should terminate.
- R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
+ // 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());
- }
+ // Read the tag from userspace.
+ u32 test_tag{};
+ R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
- // Remove the thread as a waiter from the lock owner.
- {
- KScopedSchedulerLock sl(kernel);
- KThread* owner_thread = cur_thread->GetLockOwner();
- if (owner_thread != nullptr) {
- owner_thread->RemoveWaiter(cur_thread);
- }
+ // 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)
}
}
- // Update condition variable tracking.
- {
- cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
- thread_tree.insert(*cur_thread);
- }
+ // If timeout is zero, time out.
+ R_UNLESS(timeout != 0, ResultTimedOut);
- // 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);
- }
- }
-
- // 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);
- }
+ // Update condition variable tracking.
+ cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
+ thread_tree.insert(*cur_thread);
- if (cur_thread->IsWaitingForConditionVariable()) {
- thread_tree.erase(thread_tree.iterator_to(*cur_thread));
- cur_thread->ClearConditionVariable();
- }
+ // Begin waiting.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+ cur_thread->SetMutexWaitAddressForDebugging(addr);
}
- // Get the result.
- KSynchronizationObject* dummy{};
- return cur_thread->GetWaitResult(std::addressof(dummy));
+ // Get the wait result.
+ return cur_thread->GetWaitResult();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
index 861dbd420..5e4815d08 100644
--- a/src/core/hle/kernel/k_condition_variable.h
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -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;
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index e90fc0628..cf95f0852 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -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.
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 95ec905ae..4b114ec2f 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -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.
diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp
new file mode 100644
index 000000000..a8001fffc
--- /dev/null
+++ b/src/core/hle/kernel/k_light_condition_variable.cpp
@@ -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
diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h
index fb0ad783a..5d6d7f128 100644
--- a/src/core/hle/kernel/k_light_condition_variable.h
+++ b/src/core/hle/kernel/k_light_condition_variable.h
@@ -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{};
};
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 0896e705f..4620342eb 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -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);
}
}
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h
index ad853661d..4163b8a85 100644
--- a/src/core/hle/kernel/k_light_lock.h
+++ b/src/core/hle/kernel/k_light_lock.h
@@ -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);
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index a7fdb5fb8..fd491146f 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 {
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
+
+ KernelShift = 3,
+
+ KernelRead = Read << KernelShift,
+ KernelWrite = Write << KernelShift,
+ KernelExecute = Execute << KernelShift,
+
+ NotMapped = (1 << (2 * KernelShift)),
+
+ KernelReadWrite = KernelRead | KernelWrite,
+ KernelReadExecute = KernelRead | KernelExecute,
+
+ UserRead = Read | KernelRead,
+ UserWrite = Write | KernelWrite,
+ UserExecute = Execute,
+
+ UserReadWrite = UserRead | UserWrite,
+ UserReadExecute = UserRead | UserExecute,
+
+ IpcLockChangeMask = NotMapped | UserReadWrite
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 3362fb236..0e2ae582a 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -27,6 +27,10 @@ public:
return num_pages;
}
+ constexpr std::size_t GetSize() const {
+ return GetNumPages() * PageSize;
+ }
+
private:
u64 addr{};
std::size_t num_pages{};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 9bda5c5b2..99982e5a3 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
return ResultSuccess;
}
+ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
+ KPageTable& src_page_table, VAddr src_addr) {
+ std::lock_guard lock{page_table_lock};
+
+ const std::size_t num_pages{size / PageSize};
+
+ // Check that the memory is mapped in the destination process.
+ size_t num_allocator_blocks;
+ R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
+ KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ // Check that the memory is mapped in the source process.
+ R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
+ KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Apply the memory block update.
+ block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
+ KMemoryAttribute::None);
+
+ return ResultSuccess;
+}
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
auto node{page_linked_list.Nodes().begin()};
PAddr map_addr{node->GetAddress()};
@@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
+ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
+
+ KMemoryPermission old_perm{};
+
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
+ result.IsError()) {
+ return result;
+ }
+
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
+ block->ShareToDevice(permission);
+ },
+ new_perm);
+
+ return ResultSuccess;
+}
+
+ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
+
+ KMemoryPermission old_perm{};
+
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::Locked)};
+ result.IsError()) {
+ return result;
+ }
+
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
+ block->UnshareToDevice(permission);
+ },
+ new_perm);
+
+ return ResultSuccess;
+}
+
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
@@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
+ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
+ // Get information about the first block.
+ const VAddr last_addr = addr + size - 1;
+ KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
+ KMemoryInfo info = it->GetMemoryInfo();
+
+ // If the start address isn't aligned, we need a block.
+ const size_t blocks_for_start_align =
+ (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
+
+ while (true) {
+ // Validate against the provided masks.
+ R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
+
+ // Break once we're done.
+ if (last_addr <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance our iterator.
+ it++;
+ info = it->GetMemoryInfo();
+ }
+
+ // If the end address isn't aligned, we need a block.
+ const size_t blocks_for_end_align =
+ (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
+
+ if (out_blocks_needed != nullptr) {
+ *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
+ }
+
+ return ResultSuccess;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index b7ec38f06..d784aa67e 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -33,6 +33,8 @@ public:
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapMemory(VAddr addr, std::size_t size);
@@ -55,6 +57,8 @@ public:
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
+ ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -115,6 +119,10 @@ private:
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
+ ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
std::recursive_mutex page_table_lock;
std::unique_ptr<KMemoryBlockManager> block_manager;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 1aad061e1..90dda40dc 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -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.
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 8a8c1fcbb..cb93c7e24 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -347,6 +347,7 @@ public:
void PinCurrentThread();
void UnpinCurrentThread();
+ void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {
return state_lock;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6a7d80d03..277201de4 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -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);
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 7df288438..82fcd99e7 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -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();
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index c571f2992..93c47f1b1 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -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);
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 61dc2858f..2995c492d 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -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"
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 2bd53ccbd..d4e4a6b06 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -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);
}
}
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index f168b4f21..e4c5eb74f 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -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);
- thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
- }
- // The lock/sleep is done, so we should be able to get our result.
+ // Clear the thread's synced index.
+ thread->SetSyncedIndex(-1);
- // Thread is no longer cancellable.
- thread->ClearCancellable();
-
- // For debugging only
- thread->SetWaitObjectsForDebugging({});
+ // Wait for an object to be signaled.
+ thread->BeginWait(std::addressof(wait_queue));
+ thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
+ }
- // 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);
}
}
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index 898e58e16..ec235437b 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -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;
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index db65ce79a..752592e2e 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -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,27 +1002,61 @@ 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.
+ return ResultSuccess;
+}
- // Cancel the timer.
- kernel.TimeManager().UnscheduleTimeEvent(this);
+void KThread::BeginWait(KThreadQueue* queue) {
+ // Set our state as waiting.
+ SetState(ThreadState::Waiting);
- return ResultSuccess;
+ // 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) {
@@ -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
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c77f44ad4..c8a08bd71 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -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
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
new file mode 100644
index 000000000..d5248b547
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -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
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
index 35d471dc5..ccb718e49 100644
--- a/src/core/hle/kernel/k_thread_queue.h
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -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
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 45e86a677..2e4e4cb1c 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -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) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d2ceae950..b9b423908 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -53,6 +53,7 @@ class KSharedMemoryInfo;
class KThread;
class KTransferMemory;
class KWritableEvent;
+class KCodeMemory;
class PhysicalCore;
class ServiceThread;
class Synchronization;
@@ -148,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();
@@ -271,6 +275,8 @@ public:
bool IsMulticore() const;
+ bool IsShuttingDown() const;
+
void EnterSVCProfile();
void ExitSVCProfile();
@@ -326,6 +332,8 @@ public:
return slab_heap_container->transfer_memory;
} else if constexpr (std::is_same_v<T, KWritableEvent>) {
return slab_heap_container->writeable_event;
+ } else if constexpr (std::is_same_v<T, KCodeMemory>) {
+ return slab_heap_container->code_memory;
}
}
@@ -377,6 +385,7 @@ private:
KSlabHeap<KThread> thread;
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
+ KSlabHeap<KCodeMemory> code_memory;
};
std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 6721b6276..03f3dec10 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -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();
}
}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f0cd8471e..a9f7438ea 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -18,6 +18,7 @@
#include "core/core_timing.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -31,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"
@@ -307,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) {
@@ -873,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;
}
@@ -887,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;
}
@@ -1197,6 +1203,22 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
}
}
+constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::ReadWrite;
+}
+
+constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
+}
+
+constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::None;
+}
+
+constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::None;
+}
+
} // Anonymous namespace
static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
@@ -1306,6 +1328,195 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
}
+static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = system.CurrentProcess();
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Create a new page group.
+ KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
+ KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+
+ // Map the group.
+ R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
+ KMemoryPermission::UserReadWrite));
+
+ return ResultSuccess;
+}
+
+static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = system.CurrentProcess();
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Unmap the memory.
+ R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
+
+ return ResultSuccess;
+}
+
+static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
+ LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
+ static_cast<void*>(out), address, size);
+ // Get kernel instance.
+ auto& kernel = system.Kernel();
+
+ // Validate address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Create the code memory.
+
+ KCodeMemory* code_mem = KCodeMemory::Create(kernel);
+ R_UNLESS(code_mem != nullptr, ResultOutOfResource);
+
+ // Verify that the region is in range.
+ R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
+ ResultInvalidCurrentMemory);
+
+ // Initialize the code memory.
+ R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
+
+ // Register the code memory.
+ KCodeMemory::Register(kernel, code_mem);
+
+ // Add the code memory to the handle table.
+ R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
+
+ code_mem->Close();
+
+ return ResultSuccess;
+}
+
+static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
+ VAddr address, size_t size, Svc::MemoryPermission perm) {
+
+ LOG_TRACE(Kernel_SVC,
+ "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
+ "permission=0x{:X}",
+ code_memory_handle, operation, address, size, perm);
+
+ // Validate the address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Get the code memory from its handle.
+ KScopedAutoObject code_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
+ R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
+
+ // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
+ // This enables homebrew usage of these SVCs for JIT.
+
+ // Perform the operation.
+ switch (static_cast<CodeMemoryOperation>(operation)) {
+ case CodeMemoryOperation::Map: {
+ // Check that the region is in range.
+ R_UNLESS(
+ system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory.
+ R_TRY(code_mem->Map(address, size));
+ } break;
+ case CodeMemoryOperation::Unmap: {
+ // Check that the region is in range.
+ R_UNLESS(
+ system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory.
+ R_TRY(code_mem->Unmap(address, size));
+ } break;
+ case CodeMemoryOperation::MapToOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory to its owner.
+ R_TRY(code_mem->MapToOwner(address, size, perm));
+ } break;
+ case CodeMemoryOperation::UnmapFromOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory from its owner.
+ R_TRY(code_mem->UnmapFromOwner(address, size));
+ } break;
+ default:
+ return ResultInvalidEnumValue;
+ }
+
+ return ResultSuccess;
+}
+
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -2600,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
- {0x4B, nullptr, "CreateCodeMemory"},
- {0x4C, nullptr, "ControlCodeMemory"},
+ {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
+ {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -2641,8 +2852,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
{0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
- {0x74, nullptr, "MapProcessMemory"},
- {0x75, nullptr, "UnmapProcessMemory"},
+ {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
+ {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 6e62e656f..86255fe6d 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) {
.raw);
}
+// Used by MapProcessMemory and UnmapProcessMemory
+template <ResultCode func(Core::System&, u64, u32, u64, u64)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
+ Param(system, 2), Param(system, 3))
+ .raw);
+}
+
+// Used by ControlCodeMemory
+template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
+ static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
+ static_cast<Svc::MemoryPermission>(Param(system, 4)))
+ .raw);
+}
+
template <ResultCode func(Core::System&, u32*)>
void SvcWrap64(Core::System& system) {
u32 param = 0;
@@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by CreateCodeMemory
+template <ResultCode func(Core::System&, Handle*, u64, u64)>
+void SvcWrap64(Core::System& system) {
+ u32 param_1 = 0;
+ const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
+}
+
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 8cd7279a3..aa985d820 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -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();
+ }
});
}
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index aee8d4f93..e60661fe1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -30,6 +30,7 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/apm/apm_interface.h"
#include "core/hle/service/bcat/backend/backend.h"
+#include "core/hle/service/caps/caps.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -298,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
{100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
{110, nullptr, "SetApplicationAlbumUserData"},
- {120, nullptr, "SaveCurrentScreenshot"},
+ {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
{130, nullptr, "SetRecordVolumeMuted"},
{1000, nullptr, "GetDebugStorageChannel"},
};
@@ -579,6 +580,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo
rb.Push(ResultSuccess);
}
+void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>();
+
+ LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
AppletMessageQueue::AppletMessageQueue(Core::System& system)
: service_context{system, "AppletMessageQueue"} {
on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 202d20757..2a578aea5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -151,6 +151,7 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
+ void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
enum class ScreenshotPermission : u32 {
Inherit = 0,
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 7da1f2969..981b6c996 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -96,7 +96,7 @@ private:
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output, u64* out_performance_time) const {
- const auto start_time = std::chrono::high_resolution_clock::now();
+ const auto start_time = std::chrono::steady_clock::now();
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
if (sizeof(OpusPacketHeader) > input.size()) {
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
@@ -135,7 +135,7 @@ private:
return false;
}
- const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
+ const auto end_time = std::chrono::steady_clock::now() - start_time;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
if (out_performance_time != nullptr) {
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index b18adcb9d..7254055e6 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -24,7 +24,7 @@ enum class AlbumImageOrientation {
Orientation3 = 3,
};
-enum class AlbumReportOption {
+enum class AlbumReportOption : s32 {
Disable = 0,
Enable = 1,
};
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 68c9240ae..3c36f4085 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -17,10 +17,11 @@ namespace Service::Friend {
class IFriendService final : public ServiceFramework<IFriendService> {
public:
- explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} {
+ explicit IFriendService(Core::System& system_)
+ : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetCompletionEvent"},
+ {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"},
{1, nullptr, "Cancel"},
{10100, nullptr, "GetFriendListIds"},
{10101, &IFriendService::GetFriendList, "GetFriendList"},
@@ -109,6 +110,12 @@ public:
// clang-format on
RegisterHandlers(functions);
+
+ completion_event = service_context.CreateEvent("IFriendService:CompletionEvent");
+ }
+
+ ~IFriendService() override {
+ service_context.CloseEvent(completion_event);
}
private:
@@ -129,6 +136,14 @@ private:
};
static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
+ void GetCompletionEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Friend, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(completion_event->GetReadableEvent());
+ }
+
void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) {
// This is safe to stub, as there should be no adverse consequences from reporting no
// blocked users.
@@ -179,6 +194,10 @@ private:
rb.Push<u32>(0); // Friend count
// TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
}
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* completion_event;
};
class INotificationService final : public ServiceFramework<INotificationService> {
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index a08dc9758..b24d469cf 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,7 @@
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
+#include "core/hle/service/glue/notif.h"
namespace Service::Glue {
@@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) {
// Error Context
std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
+
+ // Notification Services for application
+ std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager());
}
} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp
new file mode 100644
index 000000000..c559ec9df
--- /dev/null
+++ b/src/core/hle/service/glue/notif.cpp
@@ -0,0 +1,44 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/glue/notif.h"
+
+namespace Service::Glue {
+
+NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {500, nullptr, "RegisterAlarmSetting"},
+ {510, nullptr, "UpdateAlarmSetting"},
+ {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
+ {530, nullptr, "LoadApplicationParameter"},
+ {540, nullptr, "DeleteAlarmSetting"},
+ {1000, &NOTIF_A::Initialize, "Initialize"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+NOTIF_A::~NOTIF_A() = default;
+
+void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
+ // Returns an array of AlarmSetting
+ constexpr s32 alarm_count = 0;
+
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(alarm_count);
+}
+
+void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h
new file mode 100644
index 000000000..6ecf2015c
--- /dev/null
+++ b/src/core/hle/service/glue/notif.h
@@ -0,0 +1,25 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Glue {
+
+class NOTIF_A final : public ServiceFramework<NOTIF_A> {
+public:
+ explicit NOTIF_A(Core::System& system_);
+ ~NOTIF_A() override;
+
+private:
+ void ListAlarmSettings(Kernel::HLERequestContext& ctx);
+ void Initialize(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 04b3a68c3..2705e9dcb 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -110,7 +110,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
UpdateControllerAt(npad_type, npad_id, is_connected);
break;
case Core::HID::ControllerTriggerType::Battery: {
- if (!controller.is_connected) {
+ if (!controller.device->IsConnected()) {
return;
}
auto& shared_memory = controller.shared_memory_entry;
@@ -126,8 +126,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
}
void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
- LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
auto& controller = GetControllerFromNpadIdType(npad_id);
+ if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
+ return;
+ }
+ LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
auto& shared_memory = controller.shared_memory_entry;
if (controller_type == Core::HID::NpadStyleIndex::None) {
@@ -147,7 +150,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
break;
case Core::HID::NpadStyleIndex::Handheld:
@@ -163,21 +165,30 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break;
case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory.style_tag.joycon_dual.Assign(1);
- shared_memory.device_type.joycon_left.Assign(1);
- shared_memory.device_type.joycon_right.Assign(1);
- shared_memory.system_properties.is_vertical.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.system_properties.use_minus.Assign(1);
+ if (controller.is_dual_left_connected) {
+ shared_memory.device_type.joycon_left.Assign(1);
+ shared_memory.system_properties.use_minus.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ shared_memory.device_type.joycon_right.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
+ }
shared_memory.system_properties.use_directional_buttons.Assign(1);
+ shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
- shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+ if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+ } else if (controller.is_dual_left_connected) {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ } else {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ }
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
shared_memory.style_tag.joycon_left.Assign(1);
shared_memory.device_type.joycon_left.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
@@ -185,7 +196,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
break;
case Core::HID::NpadStyleIndex::GameCube:
@@ -197,7 +207,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::Pokeball:
shared_memory.style_tag.palma.Assign(1);
shared_memory.device_type.palma.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
break;
case Core::HID::NpadStyleIndex::NES:
shared_memory.style_tag.lark.Assign(1);
@@ -255,19 +264,7 @@ void Controller_NPad::OnInit() {
if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
// We want to support all controllers
- Core::HID::NpadStyleTag style{};
- style.handheld.Assign(1);
- style.joycon_left.Assign(1);
- style.joycon_right.Assign(1);
- style.joycon_dual.Assign(1);
- style.fullkey.Assign(1);
- style.gamecube.Assign(1);
- style.palma.Assign(1);
- style.lark.Assign(1);
- style.lucia.Assign(1);
- style.lagoon.Assign(1);
- style.lager.Assign(1);
- hid_core.SetSupportedStyleTag(style);
+ hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
}
supported_npad_id_types.resize(npad_id_list.size());
@@ -452,11 +449,15 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
case Core::HID::NpadStyleIndex::JoyconDual:
pad_state.connection_status.raw = 0;
pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_left_connected.Assign(1);
- pad_state.connection_status.is_right_connected.Assign(1);
+ if (controller.is_dual_left_connected) {
+ pad_state.connection_status.is_left_connected.Assign(1);
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ pad_state.connection_status.is_right_connected.Assign(1);
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ }
- libnx_state.connection_status.is_left_connected.Assign(1);
- libnx_state.connection_status.is_right_connected.Assign(1);
pad_state.sampling_number =
npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad.joy_dual_lifo.WriteNextEntry(pad_state);
@@ -510,7 +511,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.r_stick = pad_state.r_stick;
npad.system_ext_lifo.WriteNextEntry(pad_state);
- press_state |= static_cast<u32>(pad_state.npad_buttons.raw);
+ press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
&controller.shared_memory_entry, sizeof(NpadInternalState));
@@ -635,7 +636,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
// This buffer only is updated on handheld on HW
npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
- // Hanheld doesn't update this buffer on HW
+ // Handheld doesn't update this buffer on HW
npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
}
@@ -696,7 +697,7 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
-void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
+void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
@@ -707,6 +708,62 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
controller.shared_memory_entry.assignment_mode = assignment_mode;
}
+
+ if (!controller.device->IsConnected()) {
+ return;
+ }
+
+ if (assignment_mode == NpadJoyAssignmentMode::Dual) {
+ if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
+ DisconnectNpad(npad_id);
+ controller.is_dual_left_connected = true;
+ controller.is_dual_right_connected = false;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ return;
+ }
+ if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
+ DisconnectNpad(npad_id);
+ controller.is_dual_left_connected = false;
+ controller.is_dual_right_connected = true;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ return;
+ }
+ return;
+ }
+
+ // This is for NpadJoyAssignmentMode::Single
+
+ // Only JoyconDual get affected by this function
+ if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
+ return;
+ }
+
+ if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
+ DisconnectNpad(npad_id);
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ return;
+ }
+ if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
+ DisconnectNpad(npad_id);
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ return;
+ }
+
+ // We have two controllers connected to the same npad_id we need to split them
+ const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ DisconnectNpad(npad_id);
+ if (npad_device_type == NpadJoyDeviceType::Left) {
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ controller_2.is_dual_left_connected = false;
+ controller_2.is_dual_right_connected = true;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
+ } else {
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ controller_2.is_dual_left_connected = true;
+ controller_2.is_dual_right_connected = false;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
+ }
}
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -916,6 +973,7 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
}
auto& shared_memory_entry = controller.shared_memory_entry;
+ // Don't reset shared_memory_entry.assignment_mode this value is persistent
shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
shared_memory_entry.device_type.raw = 0;
shared_memory_entry.system_properties.raw = 0;
@@ -932,9 +990,10 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
.left = {},
.right = {},
};
- shared_memory_entry.assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
+ controller.is_dual_left_connected = true;
+ controller.is_dual_right_connected = true;
controller.is_connected = false;
controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id);
@@ -1031,19 +1090,70 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
npad_id_2);
return;
}
- auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
- auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
+ auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
+ const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
+ bool merge_controllers = false;
// If the controllers at both npad indices form a pair of left and right joycons, merge them.
// Otherwise, do nothing.
- if ((controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
- controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) ||
- (controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
- controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) {
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
+ controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
+ controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
+ !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
+ controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+
+ if (merge_controllers) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2);
+ controller_1.is_dual_left_connected = true;
+ controller_1.is_dual_right_connected = true;
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+ return;
}
+ LOG_WARNING(Service_HID,
+ "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
+ "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
+ npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
+ controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
+ controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
+ controller_2.is_dual_right_connected);
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -1072,13 +1182,18 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
const auto type_index_1 = controller_1->GetNpadStyleIndex();
const auto type_index_2 = controller_2->GetNpadStyleIndex();
+ const auto is_connected_1 = controller_1->IsConnected();
+ const auto is_connected_2 = controller_2->IsConnected();
- if (!IsControllerSupported(type_index_1) || !IsControllerSupported(type_index_2)) {
+ if (!IsControllerSupported(type_index_1) && is_connected_1) {
+ return false;
+ }
+ if (!IsControllerSupported(type_index_2) && is_connected_2) {
return false;
}
- AddNewControllerAt(type_index_2, npad_id_1);
- AddNewControllerAt(type_index_1, npad_id_2);
+ UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
+ UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
return true;
}
@@ -1149,8 +1264,8 @@ void Controller_NPad::ClearAllControllers() {
}
}
-u32 Controller_NPad::GetAndResetPressState() {
- return press_state.exchange(0);
+Core::HID::NpadButton Controller_NPad::GetAndResetPressState() {
+ return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
}
bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9fa113bb6..63281cb35 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -113,7 +113,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyAssignmentMode assignment_mode);
+ void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -179,7 +180,7 @@ public:
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
- u32 GetAndResetPressState();
+ Core::HID::NpadButton GetAndResetPressState();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
@@ -464,7 +465,10 @@ private:
std::array<VibrationData, 2> vibration{};
bool unintended_home_button_input_protection{};
bool is_connected{};
- Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+
+ // Dual joycons can have only one side connected
+ bool is_dual_left_connected{true};
+ bool is_dual_right_connected{true};
// Motion parameters
bool sixaxis_at_rest{true};
@@ -503,7 +507,7 @@ private:
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
- std::atomic<u32> press_state{};
+ std::atomic<u64> press_state{};
std::array<NpadControllerData, 10> controller_data{};
KernelHelpers::ServiceContext& service_context;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index b36689552..7163e1a4e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -293,8 +293,8 @@ Hid::Hid(Core::System& system_)
{132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
{134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
- {135, nullptr, "SetNpadCaptureButtonAssignment"},
- {136, nullptr, "ClearNpadCaptureButtonAssignment"},
+ {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
+ {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -975,35 +975,35 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
- // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- u64 npad_joy_device_type;
+ Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
+ .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
- LOG_WARNING(Service_HID,
- "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
- parameters.npad_id, parameters.applet_resource_user_id,
- parameters.npad_joy_device_type);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
+ parameters.npad_id, parameters.applet_resource_user_id,
+ parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1021,10 +1021,10 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual);
+ .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1186,6 +1186,37 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void Hid::SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::NpadStyleSet npad_styleset;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ Core::HID::NpadButton button;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
+ parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index ab0084118..d290df161 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -136,6 +136,8 @@ private:
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
+ void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
+ void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 0d7d4ad03..8314d1ec2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -20,8 +20,12 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
switch (command.group) {
case 0x0:
switch (command.cmd) {
- case 0x1:
- return Submit(input, output);
+ case 0x1: {
+ if (!fd_to_id.contains(fd)) {
+ fd_to_id[fd] = next_id++;
+ }
+ return Submit(fd, input, output);
+ }
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
- system.GPU().ClearCdmaInstance();
+ const auto iter = fd_to_id.find(fd);
+ if (iter != fd_to_id.end()) {
+ system.GPU().ClearCdmaInstance(iter->second);
+ }
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 523d96e3a..a507c4d0a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -24,6 +24,9 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+
+private:
+ u32 next_id{};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index e61261f98..8a05f0668 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -59,7 +59,8 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
+ std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -93,7 +94,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
- gpu.PushCommandBuffer(cmdlist);
+ gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 351625c17..e28c54df6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -104,13 +104,14 @@ protected:
/// Ioctl command implementations
NvResult SetNVMAPfd(const std::vector<u8>& input);
- NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+ std::unordered_map<DeviceFD, u32> fd_to_id{};
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index eac4dd530..76b39806f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -21,7 +21,10 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
case 0x0:
switch (command.cmd) {
case 0x1:
- return Submit(input, output);
+ if (!fd_to_id.contains(fd)) {
+ fd_to_id[fd] = next_id++;
+ }
+ return Submit(fd, input, output);
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
void nvhost_vic::OnOpen(DeviceFD fd) {}
void nvhost_vic::OnClose(DeviceFD fd) {
- system.GPU().ClearCdmaInstance();
+ const auto iter = fd_to_id.find(fd);
+ if (iter != fd_to_id.end()) {
+ system.GPU().ClearCdmaInstance(iter->second);
+ }
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 6d7fda9d1..c9732c037 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -23,5 +23,8 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+
+private:
+ u32 next_id{};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 3294bc0e7..5ab221fc1 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,3 +1,7 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
#pragma once
#include <array>
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 1b5aca65d..b47e3bf69 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -125,8 +125,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
metadata.Print();
- const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
- "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"};
+ const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
+ "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
+ "subsdk8", "subsdk9", "sdk"};
// Use the NSO module loader to figure out the code layout
std::size_t code_size{};
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 20f0e90f5..12446c9ac 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -19,7 +19,6 @@
namespace Core::Memory {
namespace {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
-constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
auto end_index = start_index;
@@ -61,7 +60,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
applet_resource
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
.GetAndResetPressState();
- return press_state & KEYPAD_BITMASK;
+ return static_cast<u64>(press_state & HID::NpadButton::All);
}
void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index a2541906f..816202588 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
explicit PerfStats(u64 title_id_);
~PerfStats();
- using Clock = std::chrono::high_resolution_clock;
+ using Clock = std::chrono::steady_clock;
void BeginSystemFrame();
void EndSystemFrame();
@@ -87,7 +87,7 @@ private:
class SpeedLimiter {
public:
- using Clock = std::chrono::high_resolution_clock;
+ using Clock = std::chrono::steady_clock;
void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 8b6574223..7ab4540a8 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -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);
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 8dc51d2e5..7ce1912a3 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -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;
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp
index 23b0c0ccf..4c1e5bbec 100644
--- a/src/input_common/drivers/keyboard.cpp
+++ b/src/input_common/drivers/keyboard.cpp
@@ -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.
diff --git a/src/input_common/drivers/keyboard.h b/src/input_common/drivers/keyboard.h
index ad123b136..3856c882c 100644
--- a/src/input_common/drivers/keyboard.h
+++ b/src/input_common/drivers/keyboard.h
@@ -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
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index 752118e97..aa69216c8 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -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);
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index 4a1fd2fd9..040446178 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -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.
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 90128b6cf..0cda9df62 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -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,9 +387,7 @@ void SDLDriver::CloseJoysticks() {
joystick_map.clear();
}
-SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engine_) {
- Common::SetCurrentThreadName("yuzu:input:SDL");
-
+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");
@@ -405,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;
@@ -422,6 +421,7 @@ SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engin
initialized = true;
if (start_thread) {
poll_thread = std::thread([this] {
+ Common::SetCurrentThreadName("yuzu:input:SDL");
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
@@ -492,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) {
@@ -527,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{};
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index d03ff4b84..e9a5d2e26 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -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);
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index 0e01fb0d9..5bdd5dac3 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -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;
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h
index c95a130fc..4b4e6c417 100644
--- a/src/input_common/drivers/tas_input.h
+++ b/src/input_common/drivers/tas_input.h
@@ -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};
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp
index 45b3086f6..880781825 100644
--- a/src/input_common/drivers/touch_screen.cpp
+++ b/src/input_common/drivers/touch_screen.cpp
@@ -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);
}
diff --git a/src/input_common/drivers/touch_screen.h b/src/input_common/drivers/touch_screen.h
index 25c11e8bf..bf395c40b 100644
--- a/src/input_common/drivers/touch_screen.h
+++ b/src/input_common/drivers/touch_screen.h
@@ -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.
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index d6172527c..a1ce4525d 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -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();
}
diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h
index 5d483f26b..1adc947c4 100644
--- a/src/input_common/drivers/udp_client.h
+++ b/src/input_common/drivers/udp_client.h
@@ -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();
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 77fcd655e..e23394f5f 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -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;
};
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
index 35d60bc90..ece1e3b32 100644
--- a/src/input_common/helpers/touch_from_buttons.cpp
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -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),
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 2b2105376..9c17ca4f7 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -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++;
}
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 02272b3f8..390581c94 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -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,
diff --git a/src/input_common/input_mapping.h b/src/input_common/input_mapping.h
index 44eb8ad9a..93564b5f8 100644
--- a/src/input_common/input_mapping.h
+++ b/src/input_common/input_mapping.h
@@ -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;
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 7e4eafded..7b370335f 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -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);
}
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index 573f09fde..8a0977d58 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -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);
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index bc3df80c8..4c76ce1ea 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -1,7 +1,5 @@
add_library(shader_recompiler STATIC
backend/bindings.h
- backend/glasm/emit_context.cpp
- backend/glasm/emit_context.h
backend/glasm/emit_glasm.cpp
backend/glasm/emit_glasm.h
backend/glasm/emit_glasm_barriers.cpp
@@ -22,10 +20,10 @@ add_library(shader_recompiler STATIC
backend/glasm/emit_glasm_special.cpp
backend/glasm/emit_glasm_undefined.cpp
backend/glasm/emit_glasm_warp.cpp
+ backend/glasm/glasm_emit_context.cpp
+ backend/glasm/glasm_emit_context.h
backend/glasm/reg_alloc.cpp
backend/glasm/reg_alloc.h
- backend/glsl/emit_context.cpp
- backend/glsl/emit_context.h
backend/glsl/emit_glsl.cpp
backend/glsl/emit_glsl.h
backend/glsl/emit_glsl_atomic.cpp
@@ -47,10 +45,10 @@ add_library(shader_recompiler STATIC
backend/glsl/emit_glsl_special.cpp
backend/glsl/emit_glsl_undefined.cpp
backend/glsl/emit_glsl_warp.cpp
+ backend/glsl/glsl_emit_context.cpp
+ backend/glsl/glsl_emit_context.h
backend/glsl/var_alloc.cpp
backend/glsl/var_alloc.h
- backend/spirv/emit_context.cpp
- backend/spirv/emit_context.h
backend/spirv/emit_spirv.cpp
backend/spirv/emit_spirv.h
backend/spirv/emit_spirv_atomic.cpp
@@ -72,6 +70,8 @@ add_library(shader_recompiler STATIC
backend/spirv/emit_spirv_special.cpp
backend/spirv/emit_spirv_undefined.cpp
backend/spirv/emit_spirv_warp.cpp
+ backend/spirv/spirv_emit_context.cpp
+ backend/spirv/spirv_emit_context.h
environment.h
exception.h
frontend/ir/abstract_syntax_list.h
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 004658546..42eff443f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -9,9 +9,9 @@
#include "common/div_ceil.h"
#include "common/settings.h"
#include "shader_recompiler/backend/bindings.h"
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
index e69de29bb..c0b97683e 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
@@ -0,0 +1,22 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
+
+namespace Shader::Backend::GLASM {
+
+void EmitBarrier(EmitContext& ctx) {
+ ctx.Add("BAR;");
+}
+
+void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
+ ctx.Add("MEMBAR.CTA;");
+}
+
+void EmitDeviceMemoryBarrier(EmitContext& ctx) {
+ ctx.Add("MEMBAR;");
+}
+
+} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
index 9201ccd39..3bfcbbe65 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
index bff0b7c1c..babbe6654 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index 02c9dc6d7..081b2c8e0 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/shader_info.h"
@@ -335,6 +335,35 @@ void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
ctx.Add("MOV.F result.depth.z,{};", value);
}
+void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {},invocation.groupid;", inst);
+}
+
+void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {},invocation.localid;", inst);
+}
+
+void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
+}
+
+void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
+}
+
+void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
+}
+
+void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
+ ctx.uses_y_direction = true;
+ ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
+}
+
+void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
+}
+
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
index e69de29bb..8a14fc8d9 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
@@ -0,0 +1,18 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
+
+namespace Shader::Backend::GLASM {
+
+void EmitJoin(EmitContext&) {
+ throw NotImplementedException("Join shouldn't be emitted");
+}
+
+void EmitDemoteToHelperInvocation(EmitContext& ctx) {
+ ctx.Add("KIL TR.x;");
+}
+
+} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
index ccdf1cbc8..4cff70fe4 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
index 4ed58619d..356640471 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index d325d31c7..237a5af3f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -4,8 +4,8 @@
#include <utility>
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
index 8aa494a4d..f698b8b9b 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
index e69de29bb..eed7bfec2 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
@@ -0,0 +1,26 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
+
+namespace Shader::Backend::GLASM {
+
+void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
+ ctx.Add("OR.S {},{},{};", inst, a, b);
+}
+
+void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
+ ctx.Add("AND.S {},{},{};", inst, a, b);
+}
+
+void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
+ ctx.Add("XOR.S {},{},{};", inst, a, b);
+}
+
+void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
+ ctx.Add("SEQ.S {},{},0;", inst, value);
+}
+
+} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
index af9fac7c1..f135b67f5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index 681aeda8d..86287ee3f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
@@ -17,110 +17,6 @@ namespace Shader::Backend::GLASM {
#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
-static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
- switch (phi.Type()) {
- case IR::Type::U1:
- case IR::Type::U32:
- case IR::Type::F32:
- ctx.reg_alloc.Define(phi);
- break;
- case IR::Type::U64:
- case IR::Type::F64:
- ctx.reg_alloc.LongDefine(phi);
- break;
- default:
- throw NotImplementedException("Phi node type {}", phi.Type());
- }
-}
-
-void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
- const size_t num_args{phi.NumArgs()};
- for (size_t i = 0; i < num_args; ++i) {
- ctx.reg_alloc.Consume(phi.Arg(i));
- }
- if (!phi.Definition<Id>().is_valid) {
- // The phi node wasn't forward defined
- DefinePhi(ctx, phi);
- }
-}
-
-void EmitVoid(EmitContext&) {}
-
-void EmitReference(EmitContext& ctx, const IR::Value& value) {
- ctx.reg_alloc.Consume(value);
-}
-
-void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
- IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
- if (!phi.Definition<Id>().is_valid) {
- // The phi node wasn't forward defined
- DefinePhi(ctx, phi);
- }
- const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
- const Value eval_value{ctx.reg_alloc.Consume(value)};
-
- if (phi_reg == eval_value) {
- return;
- }
- switch (phi.Flags<IR::Type>()) {
- case IR::Type::U1:
- case IR::Type::U32:
- case IR::Type::F32:
- ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
- break;
- case IR::Type::U64:
- case IR::Type::F64:
- ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
- break;
- default:
- throw NotImplementedException("Phi node type {}", phi.Type());
- }
-}
-
-void EmitJoin(EmitContext& ctx) {
- NotImplemented();
-}
-
-void EmitDemoteToHelperInvocation(EmitContext& ctx) {
- ctx.Add("KIL TR.x;");
-}
-
-void EmitBarrier(EmitContext& ctx) {
- ctx.Add("BAR;");
-}
-
-void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
- ctx.Add("MEMBAR.CTA;");
-}
-
-void EmitDeviceMemoryBarrier(EmitContext& ctx) {
- ctx.Add("MEMBAR;");
-}
-
-void EmitPrologue(EmitContext& ctx) {
- // TODO
-}
-
-void EmitEpilogue(EmitContext& ctx) {
- // TODO
-}
-
-void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
- if (stream.type == Type::U32 && stream.imm_u32 == 0) {
- ctx.Add("EMIT;");
- } else {
- ctx.Add("EMITS {};", stream);
- }
-}
-
-void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
- if (!stream.IsImmediate()) {
- LOG_WARNING(Shader_GLASM, "Stream is not immediate");
- }
- ctx.reg_alloc.Consume(stream);
- ctx.Add("ENDPRIM;");
-}
-
void EmitGetRegister(EmitContext& ctx) {
NotImplemented();
}
@@ -185,55 +81,6 @@ void EmitSetOFlag(EmitContext& ctx) {
NotImplemented();
}
-void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {},invocation.groupid;", inst);
-}
-
-void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {},invocation.localid;", inst);
-}
-
-void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
-}
-
-void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
-}
-
-void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
-}
-
-void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
- ctx.uses_y_direction = true;
- ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
-}
-
-void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
-}
-
-void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,0;", inst);
-}
-
-void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,0;", inst);
-}
-
-void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,0;", inst);
-}
-
-void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
- ctx.Add("MOV.S {}.x,0;", inst);
-}
-
-void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
- ctx.LongAdd("MOV.S64 {}.x,0;", inst);
-}
-
void EmitGetZeroFromOp(EmitContext& ctx) {
NotImplemented();
}
@@ -258,20 +105,4 @@ void EmitGetInBoundsFromOp(EmitContext& ctx) {
NotImplemented();
}
-void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
- ctx.Add("OR.S {},{},{};", inst, a, b);
-}
-
-void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
- ctx.Add("AND.S {},{},{};", inst, a, b);
-}
-
-void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
- ctx.Add("XOR.S {},{},{};", inst, a, b);
-}
-
-void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
- ctx.Add("SEQ.S {},{},0;", inst, value);
-}
-
} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
index 68fff613c..dc441c56d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
@@ -3,8 +3,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
index c1498f449..39e1c6c3a 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
@@ -3,8 +3,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
index e69de29bb..e7a5fb13a 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
@@ -0,0 +1,95 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
+#include "shader_recompiler/frontend/ir/value.h"
+
+namespace Shader::Backend::GLASM {
+
+static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
+ switch (phi.Type()) {
+ case IR::Type::U1:
+ case IR::Type::U32:
+ case IR::Type::F32:
+ ctx.reg_alloc.Define(phi);
+ break;
+ case IR::Type::U64:
+ case IR::Type::F64:
+ ctx.reg_alloc.LongDefine(phi);
+ break;
+ default:
+ throw NotImplementedException("Phi node type {}", phi.Type());
+ }
+}
+
+void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
+ const size_t num_args{phi.NumArgs()};
+ for (size_t i = 0; i < num_args; ++i) {
+ ctx.reg_alloc.Consume(phi.Arg(i));
+ }
+ if (!phi.Definition<Id>().is_valid) {
+ // The phi node wasn't forward defined
+ DefinePhi(ctx, phi);
+ }
+}
+
+void EmitVoid(EmitContext&) {}
+
+void EmitReference(EmitContext& ctx, const IR::Value& value) {
+ ctx.reg_alloc.Consume(value);
+}
+
+void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
+ IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
+ if (!phi.Definition<Id>().is_valid) {
+ // The phi node wasn't forward defined
+ DefinePhi(ctx, phi);
+ }
+ const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
+ const Value eval_value{ctx.reg_alloc.Consume(value)};
+
+ if (phi_reg == eval_value) {
+ return;
+ }
+ switch (phi.Flags<IR::Type>()) {
+ case IR::Type::U1:
+ case IR::Type::U32:
+ case IR::Type::F32:
+ ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
+ break;
+ case IR::Type::U64:
+ case IR::Type::F64:
+ ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
+ break;
+ default:
+ throw NotImplementedException("Phi node type {}", phi.Type());
+ }
+}
+
+void EmitPrologue(EmitContext&) {
+ // TODO
+}
+
+void EmitEpilogue(EmitContext&) {
+ // TODO
+}
+
+void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
+ if (stream.type == Type::U32 && stream.imm_u32 == 0) {
+ ctx.Add("EMIT;");
+ } else {
+ ctx.Add("EMITS {};", stream);
+ }
+}
+
+void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
+ if (!stream.IsImmediate()) {
+ LOG_WARNING(Shader_GLASM, "Stream is not immediate");
+ }
+ ctx.reg_alloc.Consume(stream);
+ ctx.Add("ENDPRIM;");
+}
+
+} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
index e69de29bb..875e9d991 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
@@ -0,0 +1,30 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
+
+namespace Shader::Backend::GLASM {
+
+void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,0;", inst);
+}
+
+void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,0;", inst);
+}
+
+void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,0;", inst);
+}
+
+void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
+ ctx.Add("MOV.S {}.x,0;", inst);
+}
+
+void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
+ ctx.LongAdd("MOV.S64 {}.x,0;", inst);
+}
+
+} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
index 544d475b4..32e0dd923 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
index 8fd459dfe..0401953f7 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
@@ -5,8 +5,8 @@
#include <string_view>
#include "shader_recompiler/backend/bindings.h"
-#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_context.h b/src/shader_recompiler/backend/glasm/glasm_emit_context.h
index 8433e5c00..8433e5c00 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.h
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.h
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.cpp b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
index 4c046db6e..201e428c1 100644
--- a/src/shader_recompiler/backend/glasm/reg_alloc.cpp
+++ b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
@@ -6,7 +6,7 @@
#include <fmt/format.h>
-#include "shader_recompiler/backend/glasm/emit_context.h"
+#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/backend/glasm/reg_alloc.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index 8a430d573..78b2eeaa2 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -9,9 +9,9 @@
#include "common/div_ceil.h"
#include "common/settings.h"
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
index 772acc5a4..dc377b053 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
index e1d1b558e..8a9faa394 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 3c1714e89..0f2668d9e 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
index 49a66e3ec..98cc57e58 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 4c26f3829..1920047f4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
index 53f8896be..c86465e8b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/exception.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
index eeae6562c..ce6ea1bb7 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
index d423bfb1b..b765a251b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 2f78d0267..fae2e397a 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
index 88c1d4c5e..44060df33 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
index 338ff4bd6..742fec9cf 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
index e3957491f..9fd41b4fd 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
index f420fe388..4ebdfb3bc 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#ifdef _MSC_VER
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
index 49fba9073..b1e486e5f 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
index 518b78f06..74ae345e5 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
index 67f9dad68..b8ddafe48 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
index 15bf02dd6..cace1db85 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
index cd285e2c8..6e01979b4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
@@ -4,8 +4,8 @@
#include <string_view>
-#include "shader_recompiler/backend/glsl/emit_context.h"
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 97bd59302..1de017e76 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#include "shader_recompiler/backend/bindings.h"
-#include "shader_recompiler/backend/glsl/emit_context.h"
+#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
index d9b639d29..d9b639d29 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.h
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index d7a86e270..6ce7ed12a 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -11,6 +11,7 @@
#include "common/settings.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/program.h"
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 4b25534ce..b412957c7 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -6,13 +6,11 @@
#include <vector>
-#include <sirit/sirit.h>
-
#include "common/common_types.h"
#include "shader_recompiler/backend/bindings.h"
-#include "shader_recompiler/backend/spirv/emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/profile.h"
+#include "shader_recompiler/runtime_info.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index 9af8bb9e1..0d37b405c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
index e0b52a001..9ce95a41b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index bb11f4f4e..02d1e63f7 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
index 10ff4ecab..5c3e1ee2b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index bac683ae1..ad84966b5 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -7,6 +7,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index d33486f28..1eca3aa85 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
index fd42b7a16..832de2452 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
index 61cf25f9c..0cdc46495 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 4d168a96d..d18d5f1d5 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -6,6 +6,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
index d7f1a365a..a96190bc6 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
index 50277eec3..44521f539 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
index b9a9500fc..47745f7ee 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 679ee2684..175f4be19 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -6,6 +6,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
index c5b4f4720..48caf1ffc 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
index 9a79fc7a2..330c9052c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
index 9e7eb3cb1..d96a17583 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
index c9f469e90..b5766fc52 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index cef52c56e..7034228bf 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -4,6 +4,7 @@
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 723455462..4b6f792bf 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -13,8 +13,8 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
-#include "shader_recompiler/backend/spirv/emit_context.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
+#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 63f8185d9..63f8185d9 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 8369d0d84..b4df73e8a 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -1,3 +1,7 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
#pragma once
#include <array>
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 91a30fef7..6a6325e38 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(host_shaders)
if(LIBVA_FOUND)
set_source_files_properties(command_classes/codecs/codec.cpp
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
+ list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
endif()
add_library(video_core STATIC
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 02d309170..2a532b883 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -17,12 +17,28 @@
extern "C" {
#include <libavutil/opt.h>
+#ifdef LIBVA_FOUND
+// for querying VAAPI driver information
+#include <libavutil/hwcontext_vaapi.h>
+#endif
}
namespace Tegra {
namespace {
constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
+constexpr std::array PREFERRED_GPU_DECODERS = {
+ AV_HWDEVICE_TYPE_CUDA,
+#ifdef _WIN32
+ AV_HWDEVICE_TYPE_D3D11VA,
+ AV_HWDEVICE_TYPE_DXVA2,
+#elif defined(__linux__)
+ AV_HWDEVICE_TYPE_VAAPI,
+ AV_HWDEVICE_TYPE_VDPAU,
+#endif
+ // last resort for Linux Flatpak (w/ NVIDIA)
+ AV_HWDEVICE_TYPE_VULKAN,
+};
void AVPacketDeleter(AVPacket* ptr) {
av_packet_free(&ptr);
@@ -61,83 +77,50 @@ Codec::~Codec() {
av_buffer_unref(&av_gpu_decoder);
}
-#ifdef LIBVA_FOUND
-// List all the currently loaded Linux modules
-static std::vector<std::string> ListLinuxKernelModules() {
- using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
- auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose};
- std::vector<std::string> modules{};
- if (!module_listing) {
- LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules");
- return modules;
- }
- char* buffer = nullptr;
- size_t buf_len = 0;
- while (getline(&buffer, &buf_len, module_listing.get()) != -1) {
- // format for the module listing file (sysfs)
- // <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address>
- auto line = std::string(buffer);
- // we are only interested in module names
- auto name_pos = line.find_first_of(" ");
- if (name_pos == std::string::npos) {
- continue;
- }
- modules.push_back(line.erase(name_pos));
- }
- free(buffer);
- return modules;
+// List all the currently available hwcontext in ffmpeg
+static std::vector<AVHWDeviceType> ListSupportedContexts() {
+ std::vector<AVHWDeviceType> contexts{};
+ AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
+ do {
+ current_device_type = av_hwdevice_iterate_types(current_device_type);
+ contexts.push_back(current_device_type);
+ } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
+ return contexts;
}
-#endif
bool Codec::CreateGpuAvDevice() {
-#if defined(LIBVA_FOUND)
- static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
- "i915",
- "iHD",
- "amdgpu",
- };
- AVDictionary* hwdevice_options = nullptr;
- const auto loaded_modules = ListLinuxKernelModules();
- av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
- for (const auto& driver : VAAPI_DRIVERS) {
- // first check if the target driver is loaded in the kernel
- bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(),
- [&driver](const auto& module) { return module == driver; });
- if (!found) {
- LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver);
+ static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
+ static const auto supported_contexts = ListSupportedContexts();
+ for (const auto& type : PREFERRED_GPU_DECODERS) {
+ if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
+ [&type](const auto& context) { return context == type; })) {
+ LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
- av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
- const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
- nullptr, hwdevice_options, 0);
- if (hwdevice_error >= 0) {
- LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
- av_dict_free(&hwdevice_options);
- av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
- return true;
- }
- LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
- }
- LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
- av_dict_free(&hwdevice_options);
-#endif
- static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
- static constexpr std::array GPU_DECODER_TYPES{
-#ifdef linux
- AV_HWDEVICE_TYPE_VDPAU,
-#endif
- AV_HWDEVICE_TYPE_CUDA,
-#ifdef _WIN32
- AV_HWDEVICE_TYPE_D3D11VA,
-#endif
- };
- for (const auto& type : GPU_DECODER_TYPES) {
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
if (hwdevice_res < 0) {
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
av_hwdevice_get_type_name(type), hwdevice_res);
continue;
}
+#ifdef LIBVA_FOUND
+ if (type == AV_HWDEVICE_TYPE_VAAPI) {
+ // we need to determine if this is an impersonated VAAPI driver
+ AVHWDeviceContext* hwctx =
+ static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
+ AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
+ const char* vendor_name = vaQueryVendorString(vactx->display);
+ if (strstr(vendor_name, "VDPAU backend")) {
+ // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
+ LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
+ continue;
+ } else {
+ // according to some user testing, certain vaapi driver (Intel?) could be buggy
+ // so let's log the driver name which may help the developers/supporters
+ LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
+ }
+ }
+#endif
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
if (!config) {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ab7c21a49..8788f5148 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -185,16 +185,6 @@ struct GPU::Impl {
return *dma_pusher;
}
- /// Returns a reference to the GPU CDMA pusher.
- [[nodiscard]] Tegra::CDmaPusher& CDmaPusher() {
- return *cdma_pusher;
- }
-
- /// Returns a const reference to the GPU CDMA pusher.
- [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const {
- return *cdma_pusher;
- }
-
/// Returns a reference to the underlying renderer.
[[nodiscard]] VideoCore::RendererBase& Renderer() {
return *renderer;
@@ -338,25 +328,27 @@ struct GPU::Impl {
}
/// Push GPU command buffer entries to be processed
- void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
+ void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
if (!use_nvdec) {
return;
}
- if (!cdma_pusher) {
- cdma_pusher = std::make_unique<Tegra::CDmaPusher>(gpu);
+ if (!cdma_pushers.contains(id)) {
+ cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(gpu));
}
// SubmitCommandBuffer would make the nvdec operations async, this is not currently working
// TODO(ameerj): RE proper async nvdec operation
// gpu_thread.SubmitCommandBuffer(std::move(entries));
-
- cdma_pusher->ProcessEntries(std::move(entries));
+ cdma_pushers[id]->ProcessEntries(std::move(entries));
}
/// Frees the CDMAPusher instance to free up resources
- void ClearCdmaInstance() {
- cdma_pusher.reset();
+ void ClearCdmaInstance(u32 id) {
+ const auto iter = cdma_pushers.find(id);
+ if (iter != cdma_pushers.end()) {
+ cdma_pushers.erase(iter);
+ }
}
/// Swap buffers (render frame)
@@ -659,7 +651,7 @@ struct GPU::Impl {
Core::System& system;
std::unique_ptr<Tegra::MemoryManager> memory_manager;
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
- std::unique_ptr<Tegra::CDmaPusher> cdma_pusher;
+ std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers;
std::unique_ptr<VideoCore::RendererBase> renderer;
VideoCore::RasterizerInterface* rasterizer = nullptr;
const bool use_nvdec;
@@ -811,14 +803,6 @@ const Tegra::DmaPusher& GPU::DmaPusher() const {
return impl->DmaPusher();
}
-Tegra::CDmaPusher& GPU::CDmaPusher() {
- return impl->CDmaPusher();
-}
-
-const Tegra::CDmaPusher& GPU::CDmaPusher() const {
- return impl->CDmaPusher();
-}
-
VideoCore::RendererBase& GPU::Renderer() {
return impl->Renderer();
}
@@ -887,12 +871,12 @@ void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
impl->PushGPUEntries(std::move(entries));
}
-void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
- impl->PushCommandBuffer(entries);
+void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
+ impl->PushCommandBuffer(id, entries);
}
-void GPU::ClearCdmaInstance() {
- impl->ClearCdmaInstance();
+void GPU::ClearCdmaInstance(u32 id) {
+ impl->ClearCdmaInstance(id);
}
void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c89a5d693..500411176 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -242,10 +242,10 @@ public:
void PushGPUEntries(Tegra::CommandList&& entries);
/// Push GPU command buffer entries to be processed
- void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);
+ void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries);
/// Frees the CDMAPusher instance to free up resources
- void ClearCdmaInstance();
+ void ClearCdmaInstance(u32 id);
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 37d5e6a6b..dbf1df79c 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -92,7 +92,7 @@ public:
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
- void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) {
+ void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
UNIMPLEMENTED();
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 28daacd82..f81c1b233 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -437,39 +437,29 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glBindTextureUnit(0, fxaa_texture.handle);
}
-
- // Set projection matrix
const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
- GLuint fragment_handle;
- const auto filter = Settings::values.scaling_filter.GetValue();
- switch (filter) {
- case Settings::ScalingFilter::NearestNeighbor:
- fragment_handle = present_bilinear_fragment.handle;
- break;
- case Settings::ScalingFilter::Bilinear:
- fragment_handle = present_bilinear_fragment.handle;
- break;
- case Settings::ScalingFilter::Bicubic:
- fragment_handle = present_bicubic_fragment.handle;
- break;
- case Settings::ScalingFilter::Gaussian:
- fragment_handle = present_gaussian_fragment.handle;
- break;
- case Settings::ScalingFilter::ScaleForce:
- fragment_handle = present_scaleforce_fragment.handle;
- break;
- case Settings::ScalingFilter::Fsr:
- LOG_WARNING(
- Render_OpenGL,
- "FidelityFX FSR Super Sampling is not supported in OpenGL, changing to ScaleForce");
- fragment_handle = present_scaleforce_fragment.handle;
- break;
- default:
- fragment_handle = present_bilinear_fragment.handle;
- break;
- }
+ const auto fragment_handle = [this]() {
+ switch (Settings::values.scaling_filter.GetValue()) {
+ case Settings::ScalingFilter::NearestNeighbor:
+ case Settings::ScalingFilter::Bilinear:
+ return present_bilinear_fragment.handle;
+ case Settings::ScalingFilter::Bicubic:
+ return present_bicubic_fragment.handle;
+ case Settings::ScalingFilter::Gaussian:
+ return present_gaussian_fragment.handle;
+ case Settings::ScalingFilter::ScaleForce:
+ return present_scaleforce_fragment.handle;
+ case Settings::ScalingFilter::Fsr:
+ LOG_WARNING(
+ Render_OpenGL,
+ "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
+ return present_scaleforce_fragment.handle;
+ default:
+ return present_bilinear_fragment.handle;
+ }
+ }();
program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
ortho_matrix.data());
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 9a38b6b34..cd5995897 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -4,6 +4,7 @@
#include <algorithm>
+#include "common/settings.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
@@ -335,6 +336,17 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
}
+
+VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
+ const auto& resolution = Settings::values.resolution_info;
+ const bool is_rescaled = src_image_view.IsRescaled();
+ u32 width = src_image_view.size.width;
+ u32 height = src_image_view.size.height;
+ return VkExtent2D{
+ .width = is_rescaled ? resolution.ScaleUp(width) : width,
+ .height = is_rescaled ? resolution.ScaleUp(height) : height,
+ };
+}
} // Anonymous namespace
BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
@@ -425,61 +437,52 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
}
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, u32 up_scale,
- u32 down_shift) {
+ const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass());
- Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
+ Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, u32 up_scale,
- u32 down_shift) {
+ const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass());
- Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
+ Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, u32 up_scale,
- u32 down_shift) {
+ const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass());
- Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
+ Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, u32 up_scale,
- u32 down_shift) {
+ const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass());
- Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
+ Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
- ImageView& src_image_view, u32 up_scale, u32 down_shift) {
+ const ImageView& src_image_view) {
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
- convert_abgr8_to_d24s8_frag, true);
- ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
- down_shift);
+ convert_abgr8_to_d24s8_frag);
+ Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
- ImageView& src_image_view, u32 up_scale, u32 down_shift) {
+ ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
- convert_d24s8_to_abgr8_frag, false);
- ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view, up_scale,
- down_shift);
+ convert_d24s8_to_abgr8_frag);
+ ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, u32 up_scale, u32 down_shift) {
+ const ImageView& src_image_view) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
const VkSampler sampler = *nearest_sampler;
- const VkExtent2D extent{
- .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
- .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
- };
+ const VkExtent2D extent = GetConversionExtent(src_image_view);
+
scheduler.RequestRenderpass(dst_framebuffer);
- scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
- this](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
.x = 0,
.y = 0,
@@ -563,18 +566,16 @@ void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_f
}
void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
- ImageView& src_image_view, u32 up_scale, u32 down_shift) {
+ ImageView& src_image_view) {
const VkPipelineLayout layout = *two_textures_pipeline_layout;
const VkImageView src_depth_view = src_image_view.DepthView();
const VkImageView src_stencil_view = src_image_view.StencilView();
const VkSampler sampler = *nearest_sampler;
- const VkExtent2D extent{
- .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
- .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
- };
+ const VkExtent2D extent = GetConversionExtent(src_image_view);
+
scheduler.RequestRenderpass(dst_framebuffer);
- scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, up_scale,
- down_shift, this](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent,
+ this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
.x = 0,
.y = 0,
@@ -695,11 +696,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
return *blit_depth_stencil_pipelines.back();
}
-void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
+void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
+ bool is_target_depth) {
if (pipeline) {
return;
}
- const std::array stages = MakeStages(*full_screen_vert, *convert_depth_to_float_frag);
+ VkShaderModule frag_shader =
+ is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
+ const std::array stages = MakeStages(*full_screen_vert, frag_shader);
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -712,8 +716,9 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
- .pDepthStencilState = nullptr,
- .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
+ .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
+ .pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
+ : &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *one_texture_pipeline_layout,
.renderPass = renderpass,
@@ -723,37 +728,17 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
});
}
+void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
+ ConvertPipeline(pipeline, renderpass, false);
+}
+
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
- if (pipeline) {
- return;
- }
- const std::array stages = MakeStages(*full_screen_vert, *convert_float_to_depth_frag);
- pipeline = device.GetLogical().CreateGraphicsPipeline({
- .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- .stageCount = static_cast<u32>(stages.size()),
- .pStages = stages.data(),
- .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
- .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
- .pTessellationState = nullptr,
- .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
- .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
- .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
- .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
- .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
- .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
- .layout = *one_texture_pipeline_layout,
- .renderPass = renderpass,
- .subpass = 0,
- .basePipelineHandle = VK_NULL_HANDLE,
- .basePipelineIndex = 0,
- });
+ ConvertPipeline(pipeline, renderpass, true);
}
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
- vk::ShaderModule& module, bool is_target_depth,
- bool single_texture) {
+ vk::ShaderModule& module, bool single_texture,
+ bool is_target_depth) {
if (pipeline) {
return;
}
@@ -782,13 +767,13 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
- vk::ShaderModule& module, bool single_texture) {
- ConvertPipelineEx(pipeline, renderpass, module, false, single_texture);
+ vk::ShaderModule& module) {
+ ConvertPipelineEx(pipeline, renderpass, module, false, false);
}
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
- vk::ShaderModule& module, bool single_texture) {
- ConvertPipelineEx(pipeline, renderpass, module, true, single_texture);
+ vk::ShaderModule& module) {
+ ConvertPipelineEx(pipeline, renderpass, module, true, true);
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index b1a717090..1d9f61a52 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -44,50 +44,46 @@ public:
const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
- void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
- u32 up_scale, u32 down_shift);
+ void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
- void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
- u32 up_scale, u32 down_shift);
+ void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
- void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
- u32 up_scale, u32 down_shift);
+ void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
- void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
- u32 up_scale, u32 down_shift);
+ void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
- void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
- u32 up_scale, u32 down_shift);
+ void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
- void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
- u32 up_scale, u32 down_shift);
+ void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, u32 up_scale, u32 down_shift);
+ const ImageView& src_image_view);
void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift);
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
- ImageView& src_image_view, u32 up_scale, u32 down_shift);
+ ImageView& src_image_view);
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
+ void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
+
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
- vk::ShaderModule& module, bool is_target_depth, bool single_texture);
+ vk::ShaderModule& module, bool single_texture, bool is_target_depth);
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
- vk::ShaderModule& module, bool single_texture);
+ vk::ShaderModule& module);
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
- vk::ShaderModule& module, bool single_texture);
+ vk::ShaderModule& module);
const Device& device;
VKScheduler& scheduler;
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 31adada56..751e4792b 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -162,7 +162,7 @@ struct FormatTuple {
{VK_FORMAT_UNDEFINED}, // R16_SINT
{VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM
{VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT
- {VK_FORMAT_UNDEFINED}, // R16G16_UINT
+ {VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT
{VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT
{VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM
{VK_FORMAT_UNDEFINED}, // R32G32B32_FLOAT
@@ -176,8 +176,8 @@ struct FormatTuple {
{VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT
{VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT
{VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM
- {VK_FORMAT_UNDEFINED}, // ASTC_2D_8X5_UNORM
- {VK_FORMAT_UNDEFINED}, // ASTC_2D_5X4_UNORM
+ {VK_FORMAT_ASTC_8x5_UNORM_BLOCK}, // ASTC_2D_8X5_UNORM
+ {VK_FORMAT_ASTC_5x4_UNORM_BLOCK}, // ASTC_2D_5X4_UNORM
{VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // B8G8R8A8_SRGB
{VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // BC1_RGBA_SRGB
{VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 1e447e621..c71a1f44d 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -391,28 +391,23 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
.offset = {0, 0},
.extent = size,
};
- const auto filter = Settings::values.scaling_filter.GetValue();
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
- switch (filter) {
- case Settings::ScalingFilter::NearestNeighbor:
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
- break;
- case Settings::ScalingFilter::Bilinear:
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
- break;
- case Settings::ScalingFilter::Bicubic:
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline);
- break;
- case Settings::ScalingFilter::Gaussian:
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *gaussian_pipeline);
- break;
- case Settings::ScalingFilter::ScaleForce:
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline);
- break;
- default:
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
- break;
- }
+ auto graphics_pipeline = [this]() {
+ switch (Settings::values.scaling_filter.GetValue()) {
+ case Settings::ScalingFilter::NearestNeighbor:
+ case Settings::ScalingFilter::Bilinear:
+ return *bilinear_pipeline;
+ case Settings::ScalingFilter::Bicubic:
+ return *bicubic_pipeline;
+ case Settings::ScalingFilter::Gaussian:
+ return *gaussian_pipeline;
+ case Settings::ScalingFilter::ScaleForce:
+ return *scaleforce_pipeline;
+ default:
+ return *bilinear_pipeline;
+ }
+ }();
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 197cba8e3..1941170cb 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1057,37 +1057,37 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
});
}
-void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view,
- bool rescaled) {
- const u32 up_scale = rescaled ? resolution.up_scale : 1;
- const u32 down_shift = rescaled ? resolution.down_shift : 0;
+void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
switch (dst_view.format) {
case PixelFormat::R16_UNORM:
if (src_view.format == PixelFormat::D16_UNORM) {
- return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift);
+ return blit_image_helper.ConvertD16ToR16(dst, src_view);
}
break;
case PixelFormat::A8B8G8R8_UNORM:
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
- return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift);
+ return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
}
break;
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT) {
- return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift);
+ return blit_image_helper.ConvertD32ToR32(dst, src_view);
}
break;
case PixelFormat::D16_UNORM:
if (src_view.format == PixelFormat::R16_UNORM) {
- return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift);
+ return blit_image_helper.ConvertR16ToD16(dst, src_view);
}
break;
case PixelFormat::S8_UINT_D24_UNORM:
- return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift);
+ if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
+ src_view.format == PixelFormat::B8G8R8A8_UNORM) {
+ return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
+ }
break;
case PixelFormat::D32_FLOAT:
if (src_view.format == PixelFormat::R32_FLOAT) {
- return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift);
+ return blit_image_helper.ConvertR32ToD32(dst, src_view);
}
break;
default:
@@ -1329,6 +1329,10 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
}
}
+bool Image::IsRescaled() const noexcept {
+ return True(flags & ImageFlagBits::Rescaled);
+}
+
bool Image::ScaleUp(bool ignore) {
if (True(flags & ImageFlagBits::Rescaled)) {
return false;
@@ -1469,7 +1473,8 @@ bool Image::BlitScaleHelper(bool scale_up) {
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
ImageId image_id_, Image& image)
: VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device},
- image_handle{image.Handle()}, samples{ConvertSampleCount(image.info.num_samples)} {
+ src_image{&image}, image_handle{image.Handle()},
+ samples(ConvertSampleCount(image.info.num_samples)) {
using Shader::TextureType;
const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
@@ -1607,6 +1612,13 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
return *view;
}
+bool ImageView::IsRescaled() const noexcept {
+ if (!src_image) {
+ return false;
+ }
+ return src_image->IsRescaled();
+}
+
vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) {
return device->GetLogical().CreateImageView({
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 753e3e8a1..c592f2666 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -65,7 +65,7 @@ public:
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
- void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled);
+ void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view);
bool CanAccelerateImageUpload(Image&) const noexcept {
return false;
@@ -139,6 +139,8 @@ public:
return std::exchange(initialized, true);
}
+ bool IsRescaled() const noexcept;
+
bool ScaleUp(bool ignore = false);
bool ScaleDown(bool ignore = false);
@@ -189,6 +191,8 @@ public:
[[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
Shader::ImageFormat image_format);
+ [[nodiscard]] bool IsRescaled() const noexcept;
+
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
return *image_views[static_cast<size_t>(texture_type)];
}
@@ -222,6 +226,8 @@ private:
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
const Device* device = nullptr;
+ const Image* src_image{};
+
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
std::unique_ptr<StorageViews> storage_views;
vk::ImageView depth_view;
diff --git a/src/video_core/shader_notify.cpp b/src/video_core/shader_notify.cpp
index dc6995b46..bcaf5f575 100644
--- a/src/video_core/shader_notify.cpp
+++ b/src/video_core/shader_notify.cpp
@@ -18,7 +18,7 @@ int ShaderNotify::ShadersBuilding() noexcept {
const int now_complete = num_complete.load(std::memory_order::relaxed);
const int now_building = num_building.load(std::memory_order::relaxed);
if (now_complete == now_building) {
- const auto now = std::chrono::high_resolution_clock::now();
+ const auto now = std::chrono::steady_clock::now();
if (completed && num_complete == num_when_completed) {
if (now - complete_time > TIME_TO_STOP_REPORTING) {
report_base = now_complete;
diff --git a/src/video_core/shader_notify.h b/src/video_core/shader_notify.h
index ad363bfb5..4d8d52071 100644
--- a/src/video_core/shader_notify.h
+++ b/src/video_core/shader_notify.h
@@ -28,6 +28,6 @@ private:
bool completed{};
int num_when_completed{};
- std::chrono::high_resolution_clock::time_point complete_time;
+ std::chrono::steady_clock::time_point complete_time;
};
} // namespace VideoCore
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index ddfb726fe..afa807d5d 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -139,6 +139,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
return PixelFormat::D16_UNORM;
case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR):
return PixelFormat::S8_UINT_D24_UNORM;
+ case Hash(TextureFormat::S8D24, UINT, UNORM, UINT, UINT, LINEAR):
+ return PixelFormat::S8_UINT_D24_UNORM;
case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
return PixelFormat::S8_UINT_D24_UNORM;
case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 565b99254..2e19fced2 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1122,7 +1122,7 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
break;
}
if (can_be_depth_blit) {
- const ImageBase* const dst_image = src_id ? &slot_images[src_id] : nullptr;
+ const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr;
DeduceBlitImages(dst_info, src_info, dst_image, src_image);
if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) {
continue;
@@ -1135,8 +1135,16 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{});
}
} while (has_deleted_images);
- if (GetFormatType(dst_info.format) != SurfaceType::ColorTexture) {
- // Make sure the images are depth and/or stencil textures.
+ 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) ||
+ !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;
src_id = FindOrInsertImage(src_info, src_addr, RelaxedOptions{});
@@ -1847,9 +1855,20 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
.height = std::min(dst_view.size.height, src_view.size.height),
.depth = std::min(dst_view.size.depth, src_view.size.depth),
};
- UNIMPLEMENTED_IF(copy.extent != expected_size);
+ const Extent3D scaled_extent = [is_rescaled, expected_size]() {
+ if (!is_rescaled) {
+ return expected_size;
+ }
+ const auto& resolution = Settings::values.resolution_info;
+ return Extent3D{
+ .width = resolution.ScaleUp(expected_size.width),
+ .height = resolution.ScaleUp(expected_size.height),
+ .depth = expected_size.depth,
+ };
+ }();
+ UNIMPLEMENTED_IF(copy.extent != scaled_extent);
- runtime.ConvertImage(dst_framebuffer, dst_view, src_view, is_rescaled);
+ runtime.ConvertImage(dst_framebuffer, dst_view, src_view);
}
}
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 70c52aaac..7bf5b6578 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -130,6 +130,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_R16G16_UNORM,
VK_FORMAT_R16G16_SNORM,
VK_FORMAT_R16G16_SFLOAT,
+ VK_FORMAT_R16G16_UINT,
VK_FORMAT_R16G16_SINT,
VK_FORMAT_R16_UNORM,
VK_FORMAT_R16_SNORM,
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index a44815e71..732e8c276 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -152,6 +152,8 @@ add_executable(yuzu
main.ui
uisettings.cpp
uisettings.h
+ util/controller_navigation.cpp
+ util/controller_navigation.h
util/limitable_input_dialog.cpp
util/limitable_input_dialog.h
util/overlay_dialog.cpp
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 589e0577a..c5685db2e 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -139,7 +139,6 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
DisableUnsupportedPlayers();
for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
- system.HIDCore().GetEmulatedControllerByIndex(player_index)->EnableConfiguration();
SetEmulatedControllers(player_index);
}
@@ -218,7 +217,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
resize(0, 0);
}
-QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
+QtControllerSelectorDialog::~QtControllerSelectorDialog() {
+ system.HIDCore().DisableAllControllerConfiguration();
+}
int QtControllerSelectorDialog::exec() {
if (parameters_met && parameters.enable_single_mode) {
@@ -234,12 +235,11 @@ void QtControllerSelectorDialog::ApplyConfiguration() {
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
- for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
- system.HIDCore().GetEmulatedControllerByIndex(player_index)->DisableConfiguration();
- }
}
void QtControllerSelectorDialog::LoadConfiguration() {
+ system.HIDCore().EnableAllControllerConfiguration();
+
const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h
index cc343e5ae..7ab9ced3d 100644
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -7,7 +7,6 @@
#include <array>
#include <memory>
#include <QDialog>
-#include "core/core.h"
#include "core/frontend/applets/controller.h"
class GMainWindow;
@@ -32,8 +31,9 @@ class System;
}
namespace Core::HID {
+class HIDCore;
enum class NpadStyleIndex : u8;
-}
+} // namespace Core::HID
class QtControllerSelectorDialog final : public QDialog {
Q_OBJECT
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index a56638e21..7b19f1f8d 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <mutex>
+#include <QApplication>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
@@ -16,6 +17,7 @@
#include "core/hle/lock.h"
#include "yuzu/applets/qt_profile_select.h"
#include "yuzu/main.h"
+#include "yuzu/util/controller_navigation.h"
namespace {
QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
@@ -45,7 +47,7 @@ QPixmap GetIcon(Common::UUID uuid) {
}
} // Anonymous namespace
-QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
+QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent)
: QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
outer_layout = new QVBoxLayout;
@@ -65,6 +67,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
tree_view = new QTreeView;
item_model = new QStandardItemModel(tree_view);
tree_view->setModel(item_model);
+ controller_navigation = new ControllerNavigation(hid_core, this);
tree_view->setAlternatingRowColors(true);
tree_view->setSelectionMode(QHeaderView::SingleSelection);
@@ -91,6 +94,14 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
scroll_area->setLayout(layout);
connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
+ connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
+ [this](Qt::Key key) {
+ if (!this->isActiveWindow()) {
+ return;
+ }
+ QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
+ QCoreApplication::postEvent(tree_view, event);
+ });
const auto& profiles = profile_manager->GetAllUsers();
for (const auto& user : profiles) {
@@ -113,7 +124,9 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
resize(550, 400);
}
-QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
+QtProfileSelectionDialog::~QtProfileSelectionDialog() {
+ controller_navigation->UnloadController();
+};
int QtProfileSelectionDialog::exec() {
// Skip profile selection when there's only one.
diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h
index 4e9037488..56496ed31 100644
--- a/src/yuzu/applets/qt_profile_select.h
+++ b/src/yuzu/applets/qt_profile_select.h
@@ -11,6 +11,7 @@
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
+class ControllerNavigation;
class GMainWindow;
class QDialogButtonBox;
class QGraphicsScene;
@@ -20,11 +21,15 @@ class QStandardItem;
class QStandardItemModel;
class QVBoxLayout;
+namespace Core::HID {
+class HIDCore;
+} // namespace Core::HID
+
class QtProfileSelectionDialog final : public QDialog {
Q_OBJECT
public:
- explicit QtProfileSelectionDialog(QWidget* parent);
+ explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent);
~QtProfileSelectionDialog() override;
int exec() override;
@@ -51,6 +56,7 @@ private:
QDialogButtonBox* buttons;
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
+ ControllerNavigation* controller_navigation = nullptr;
};
class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index de7f98c4f..c3857fc98 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -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()));
+ 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()));
+ // 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();
}
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 463d500c2..0f679c37e 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -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);
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 16284d5a6..8a8be8e40 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -246,15 +246,15 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
- auto* emulated_controller_hanheld =
+ auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
emulated_controller_p1->SaveCurrentConfig();
emulated_controller_p1->EnableConfiguration();
- emulated_controller_hanheld->SaveCurrentConfig();
- emulated_controller_hanheld->EnableConfiguration();
- if (emulated_controller_hanheld->IsConnected(true)) {
+ emulated_controller_handheld->SaveCurrentConfig();
+ emulated_controller_handheld->EnableConfiguration();
+ if (emulated_controller_handheld->IsConnected(true)) {
emulated_controller_p1->Disconnect();
- emulated_controller = emulated_controller_hanheld;
+ emulated_controller = emulated_controller_handheld;
} else {
emulated_controller = emulated_controller_p1;
}
@@ -590,19 +590,19 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
- auto* emulated_controller_hanheld =
+ auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
bool is_connected = emulated_controller->IsConnected(true);
emulated_controller_p1->SetNpadStyleIndex(type);
- emulated_controller_hanheld->SetNpadStyleIndex(type);
+ emulated_controller_handheld->SetNpadStyleIndex(type);
if (is_connected) {
if (type == Core::HID::NpadStyleIndex::Handheld) {
emulated_controller_p1->Disconnect();
- emulated_controller_hanheld->Connect();
- emulated_controller = emulated_controller_hanheld;
+ emulated_controller_handheld->Connect();
+ emulated_controller = emulated_controller_handheld;
} else {
- emulated_controller_hanheld->Disconnect();
+ emulated_controller_handheld->Disconnect();
emulated_controller_p1->Connect();
emulated_controller = emulated_controller_p1;
}
@@ -650,10 +650,10 @@ ConfigureInputPlayer::~ConfigureInputPlayer() {
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
- auto* emulated_controller_hanheld =
+ auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
emulated_controller_p1->DisableConfiguration();
- emulated_controller_hanheld->DisableConfiguration();
+ emulated_controller_handheld->DisableConfiguration();
} else {
emulated_controller->DisableConfiguration();
}
@@ -663,14 +663,14 @@ void ConfigureInputPlayer::ApplyConfiguration() {
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
- auto* emulated_controller_hanheld =
+ auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
emulated_controller_p1->DisableConfiguration();
emulated_controller_p1->SaveCurrentConfig();
emulated_controller_p1->EnableConfiguration();
- emulated_controller_hanheld->DisableConfiguration();
- emulated_controller_hanheld->SaveCurrentConfig();
- emulated_controller_hanheld->EnableConfiguration();
+ emulated_controller_handheld->DisableConfiguration();
+ emulated_controller_handheld->SaveCurrentConfig();
+ emulated_controller_handheld->EnableConfiguration();
return;
}
emulated_controller->DisableConfiguration();
@@ -907,88 +907,79 @@ void ConfigureInputPlayer::UpdateUI() {
}
void ConfigureInputPlayer::SetConnectableControllers() {
- const auto add_controllers = [this](bool enable_all,
- Core::HID::NpadStyleTag npad_style_set = {}) {
- index_controller_type_pairs.clear();
- ui->comboControllerType->clear();
-
- if (enable_all || npad_style_set.fullkey == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::ProController);
- ui->comboControllerType->addItem(tr("Pro Controller"));
- }
-
- if (enable_all || npad_style_set.joycon_dual == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::JoyconDual);
- ui->comboControllerType->addItem(tr("Dual Joycons"));
- }
+ Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag();
+ index_controller_type_pairs.clear();
+ ui->comboControllerType->clear();
- if (enable_all || npad_style_set.joycon_left == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::JoyconLeft);
- ui->comboControllerType->addItem(tr("Left Joycon"));
- }
+ if (npad_style_set.fullkey == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::ProController);
+ ui->comboControllerType->addItem(tr("Pro Controller"));
+ }
- if (enable_all || npad_style_set.joycon_right == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::JoyconRight);
- ui->comboControllerType->addItem(tr("Right Joycon"));
- }
+ if (npad_style_set.joycon_dual == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::JoyconDual);
+ ui->comboControllerType->addItem(tr("Dual Joycons"));
+ }
- if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::Handheld);
- ui->comboControllerType->addItem(tr("Handheld"));
- }
+ if (npad_style_set.joycon_left == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::JoyconLeft);
+ ui->comboControllerType->addItem(tr("Left Joycon"));
+ }
- if (enable_all || npad_style_set.gamecube == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::GameCube);
- ui->comboControllerType->addItem(tr("GameCube Controller"));
- }
+ if (npad_style_set.joycon_right == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::JoyconRight);
+ ui->comboControllerType->addItem(tr("Right Joycon"));
+ }
- // Disable all unsupported controllers
- if (!Settings::values.enable_all_controllers) {
- return;
- }
- if (enable_all || npad_style_set.palma == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::Pokeball);
- ui->comboControllerType->addItem(tr("Poke Ball Plus"));
- }
+ if (player_index == 0 && npad_style_set.handheld == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::Handheld);
+ ui->comboControllerType->addItem(tr("Handheld"));
+ }
- if (enable_all || npad_style_set.lark == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::NES);
- ui->comboControllerType->addItem(tr("NES Controller"));
- }
+ if (npad_style_set.gamecube == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::GameCube);
+ ui->comboControllerType->addItem(tr("GameCube Controller"));
+ }
- if (enable_all || npad_style_set.lucia == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::SNES);
- ui->comboControllerType->addItem(tr("SNES Controller"));
- }
+ // Disable all unsupported controllers
+ if (!Settings::values.enable_all_controllers) {
+ return;
+ }
+ if (npad_style_set.palma == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::Pokeball);
+ ui->comboControllerType->addItem(tr("Poke Ball Plus"));
+ }
- if (enable_all || npad_style_set.lagoon == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::N64);
- ui->comboControllerType->addItem(tr("N64 Controller"));
- }
+ if (npad_style_set.lark == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::NES);
+ ui->comboControllerType->addItem(tr("NES Controller"));
+ }
- if (enable_all || npad_style_set.lager == 1) {
- index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
- Core::HID::NpadStyleIndex::SegaGenesis);
- ui->comboControllerType->addItem(tr("Sega Genesis"));
- }
- };
+ if (npad_style_set.lucia == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::SNES);
+ ui->comboControllerType->addItem(tr("SNES Controller"));
+ }
- if (!is_powered_on) {
- add_controllers(true);
- return;
+ if (npad_style_set.lagoon == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::N64);
+ ui->comboControllerType->addItem(tr("N64 Controller"));
}
- add_controllers(false, hid_core.GetSupportedStyleTag());
+ if (npad_style_set.lager == 1) {
+ index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+ Core::HID::NpadStyleIndex::SegaGenesis);
+ ui->comboControllerType->addItem(tr("Sega Genesis"));
+ }
}
Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index a8b254199..33110685a 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -163,7 +163,7 @@ void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
}
void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
- const auto wheel_position = ev->pos();
+ const auto wheel_position = ev->position().toPoint();
MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale,
ev->angleDelta().y() / 120);
ev->accept();
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 2af95dbe5..8b5c4a10a 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -17,6 +17,7 @@
#include <fmt/format.h>
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "yuzu/compatibility_list.h"
@@ -25,6 +26,7 @@
#include "yuzu/game_list_worker.h"
#include "yuzu/main.h"
#include "yuzu/uisettings.h"
+#include "yuzu/util/controller_navigation.h"
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
: QObject(parent), gamelist{gamelist} {}
@@ -171,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
@@ -312,6 +318,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
this->main_window = parent;
layout = new QVBoxLayout;
tree_view = new QTreeView;
+ controller_navigation = new ControllerNavigation(system.HIDCore(), this);
search_field = new GameListSearchField(this);
item_model = new QStandardItemModel(tree_view);
tree_view->setModel(item_model);
@@ -341,6 +348,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
+ connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
+ [this](Qt::Key key) {
+ // Avoid pressing buttons while playing
+ if (system.IsPoweredOn()) {
+ return;
+ }
+ if (!this->isActiveWindow()) {
+ return;
+ }
+ QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
+ QCoreApplication::postEvent(tree_view, event);
+ });
// We must register all custom types with the Qt Automoc system so that we are able to use
// it with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -353,7 +372,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
setLayout(layout);
}
+void GameList::UnloadController() {
+ controller_navigation->UnloadController();
+}
+
GameList::~GameList() {
+ UnloadController();
emit ShouldCancelWorker();
}
@@ -438,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);
}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 675469e66..a94ea1477 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -24,6 +24,7 @@
#include "uisettings.h"
#include "yuzu/compatibility_list.h"
+class ControllerNavigation;
class GameListWorker;
class GameListSearchField;
class GameListDir;
@@ -88,6 +89,9 @@ public:
void SaveInterfaceLayout();
void LoadInterfaceLayout();
+ /// Disables events from the emulated controller
+ void UnloadController();
+
static const QStringList supported_file_extensions;
signals:
@@ -143,6 +147,7 @@ private:
QStandardItemModel* item_model = nullptr;
GameListWorker* current_worker = nullptr;
QFileSystemWatcher* watcher = nullptr;
+ ControllerNavigation* controller_navigation = nullptr;
CompatibilityList compatibility_list;
friend class GameListSearchField;
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index ae842306c..b001b8c23 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -136,7 +136,7 @@ void LoadingScreen::OnLoadComplete() {
void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total) {
using namespace std::chrono;
- const auto now = high_resolution_clock::now();
+ const auto now = steady_clock::now();
// reset the timer if the stage changes
if (stage != previous_stage) {
ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage]));
@@ -160,7 +160,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
// If theres a drastic slowdown in the rate, then display an estimate
if (now - previous_time > milliseconds{50} || slow_shader_compile_start) {
if (!slow_shader_compile_start) {
- slow_shader_start = high_resolution_clock::now();
+ slow_shader_start = steady_clock::now();
slow_shader_compile_start = true;
slow_shader_first_value = value;
}
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
index 801d08e1a..29155a77c 100644
--- a/src/yuzu/loading_screen.h
+++ b/src/yuzu/loading_screen.h
@@ -84,8 +84,8 @@ private:
// shaders, it will start quickly but end slow if new shaders were added since previous launch.
// These variables are used to detect the change in speed so we can generate an ETA
bool slow_shader_compile_start = false;
- std::chrono::high_resolution_clock::time_point slow_shader_start;
- std::chrono::high_resolution_clock::time_point previous_time;
+ std::chrono::steady_clock::time_point slow_shader_start;
+ std::chrono::steady_clock::time_point previous_time;
std::size_t slow_shader_first_value = 0;
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9bd0db10a..b7bb43348 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -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();
@@ -449,7 +451,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
}
void GMainWindow::ProfileSelectorSelectProfile() {
- QtProfileSelectionDialog dialog(this);
+ QtProfileSelectionDialog dialog(system->HIDCore(), this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
Qt::WindowCloseButtonHint);
@@ -1082,14 +1084,15 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
state != Qt::ApplicationActive) {
LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state);
}
- if (ui->action_Pause->isEnabled() &&
- (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
- auto_paused = true;
- OnPauseGame();
- } else if (emulation_running && !emu_thread->IsRunning() && auto_paused &&
- state == Qt::ApplicationActive) {
- auto_paused = false;
- OnStartGame();
+ if (emulation_running) {
+ if (emu_thread->IsRunning() &&
+ (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
+ auto_paused = true;
+ OnPauseGame();
+ } else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
+ auto_paused = false;
+ OnStartGame();
+ }
}
}
@@ -1301,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);
@@ -1346,7 +1346,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
}
void GMainWindow::SelectAndSetCurrentUser() {
- QtProfileSelectionDialog dialog(this);
+ QtProfileSelectionDialog dialog(system->HIDCore(), this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
@@ -1516,6 +1516,9 @@ void GMainWindow::ShutdownGame() {
input_subsystem->GetTas()->Stop();
OnTasStateChanged();
+ // Enable all controllers
+ system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
+
render_window->removeEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, false);
@@ -1608,7 +1611,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
if (has_user_save) {
// User save data
const auto select_profile = [this] {
- QtProfileSelectionDialog dialog(this);
+ QtProfileSelectionDialog dialog(system->HIDCore(), this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
@@ -3376,7 +3379,10 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UpdateUISettings();
game_list->SaveInterfaceLayout();
hotkey_registry.SaveHotkeys();
+
+ // Unload controllers early
controller_dialog->UnloadController();
+ game_list->UnloadController();
system->HIDCore().UnloadInputDevices();
// Shutdown session if the emu thread is active...
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 936914ef3..a610e7e25 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -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;
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp
new file mode 100644
index 000000000..86fb28b9f
--- /dev/null
+++ b/src/yuzu/util/controller_navigation.cpp
@@ -0,0 +1,177 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include "common/settings_input.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "yuzu/util/controller_navigation.h"
+
+ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) {
+ player1_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); },
+ .is_npad_service = false,
+ };
+ player1_callback_key = player1_controller->SetCallback(engine_callback);
+ handheld_callback_key = handheld_controller->SetCallback(engine_callback);
+ is_controller_set = true;
+}
+
+ControllerNavigation::~ControllerNavigation() {
+ UnloadController();
+}
+
+void ControllerNavigation::UnloadController() {
+ if (is_controller_set) {
+ player1_controller->DeleteCallback(player1_callback_key);
+ handheld_controller->DeleteCallback(handheld_callback_key);
+ is_controller_set = false;
+ }
+}
+
+void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_button,
+ Qt::Key key) {
+ if (button_values[native_button].value && !button_values[native_button].locked) {
+ emit TriggerKeyboardEvent(key);
+ }
+}
+
+void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
+ std::lock_guard lock{mutex};
+ if (type == Core::HID::ControllerTriggerType::Button) {
+ ControllerUpdateButton();
+ return;
+ }
+
+ if (type == Core::HID::ControllerTriggerType::Stick) {
+ ControllerUpdateStick();
+ return;
+ }
+}
+
+void ControllerNavigation::ControllerUpdateButton() {
+ const auto controller_type = player1_controller->GetNpadStyleIndex();
+ const auto& player1_buttons = player1_controller->GetButtonsValues();
+ const auto& handheld_buttons = handheld_controller->GetButtonsValues();
+
+ for (std::size_t i = 0; i < player1_buttons.size(); ++i) {
+ const bool button = player1_buttons[i].value || handheld_buttons[i].value;
+ // Trigger only once
+ button_values[i].locked = button == button_values[i].value;
+ button_values[i].value = button;
+ }
+
+ switch (controller_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::Handheld:
+ case Core::HID::NpadStyleIndex::GameCube:
+ TriggerButton(Settings::NativeButton::A, Qt::Key_Enter);
+ TriggerButton(Settings::NativeButton::B, Qt::Key_Escape);
+ TriggerButton(Settings::NativeButton::DDown, Qt::Key_Down);
+ TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Left);
+ TriggerButton(Settings::NativeButton::DRight, Qt::Key_Right);
+ TriggerButton(Settings::NativeButton::DUp, Qt::Key_Up);
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ TriggerButton(Settings::NativeButton::DDown, Qt::Key_Enter);
+ TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Escape);
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ TriggerButton(Settings::NativeButton::X, Qt::Key_Enter);
+ TriggerButton(Settings::NativeButton::A, Qt::Key_Escape);
+ break;
+ default:
+ break;
+ }
+}
+
+void ControllerNavigation::ControllerUpdateStick() {
+ const auto controller_type = player1_controller->GetNpadStyleIndex();
+ const auto& player1_sticks = player1_controller->GetSticksValues();
+ const auto& handheld_sticks = player1_controller->GetSticksValues();
+ bool update = false;
+
+ for (std::size_t i = 0; i < player1_sticks.size(); ++i) {
+ const Common::Input::StickStatus stick{
+ .left = player1_sticks[i].left || handheld_sticks[i].left,
+ .right = player1_sticks[i].right || handheld_sticks[i].right,
+ .up = player1_sticks[i].up || handheld_sticks[i].up,
+ .down = player1_sticks[i].down || handheld_sticks[i].down,
+ };
+ // Trigger only once
+ if (stick.down != stick_values[i].down || stick.left != stick_values[i].left ||
+ stick.right != stick_values[i].right || stick.up != stick_values[i].up) {
+ update = true;
+ }
+ stick_values[i] = stick;
+ }
+
+ if (!update) {
+ return;
+ }
+
+ switch (controller_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::Handheld:
+ case Core::HID::NpadStyleIndex::GameCube:
+ if (stick_values[Settings::NativeAnalog::LStick].down) {
+ emit TriggerKeyboardEvent(Qt::Key_Down);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::LStick].left) {
+ emit TriggerKeyboardEvent(Qt::Key_Left);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::LStick].right) {
+ emit TriggerKeyboardEvent(Qt::Key_Right);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::LStick].up) {
+ emit TriggerKeyboardEvent(Qt::Key_Up);
+ return;
+ }
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ if (stick_values[Settings::NativeAnalog::LStick].left) {
+ emit TriggerKeyboardEvent(Qt::Key_Down);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::LStick].up) {
+ emit TriggerKeyboardEvent(Qt::Key_Left);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::LStick].down) {
+ emit TriggerKeyboardEvent(Qt::Key_Right);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::LStick].right) {
+ emit TriggerKeyboardEvent(Qt::Key_Up);
+ return;
+ }
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ if (stick_values[Settings::NativeAnalog::RStick].right) {
+ emit TriggerKeyboardEvent(Qt::Key_Down);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::RStick].down) {
+ emit TriggerKeyboardEvent(Qt::Key_Left);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::RStick].up) {
+ emit TriggerKeyboardEvent(Qt::Key_Right);
+ return;
+ }
+ if (stick_values[Settings::NativeAnalog::RStick].left) {
+ emit TriggerKeyboardEvent(Qt::Key_Up);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/yuzu/util/controller_navigation.h b/src/yuzu/util/controller_navigation.h
new file mode 100644
index 000000000..7c616a088
--- /dev/null
+++ b/src/yuzu/util/controller_navigation.h
@@ -0,0 +1,51 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+#include <QKeyEvent>
+#include <QObject>
+
+#include "common/input.h"
+#include "common/settings_input.h"
+
+namespace Core::HID {
+using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
+using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
+enum class ControllerTriggerType;
+class EmulatedController;
+class HIDCore;
+} // namespace Core::HID
+
+class ControllerNavigation : public QObject {
+ Q_OBJECT
+
+public:
+ explicit ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent = nullptr);
+ ~ControllerNavigation();
+
+ /// Disables events from the emulated controller
+ void UnloadController();
+
+signals:
+ void TriggerKeyboardEvent(Qt::Key key);
+
+private:
+ void TriggerButton(Settings::NativeButton::Values native_button, Qt::Key key);
+ void ControllerUpdateEvent(Core::HID::ControllerTriggerType type);
+
+ void ControllerUpdateButton();
+
+ void ControllerUpdateStick();
+
+ Core::HID::ButtonValues button_values{};
+ Core::HID::SticksValues stick_values{};
+
+ int player1_callback_key{};
+ int handheld_callback_key{};
+ bool is_controller_set{};
+ mutable std::mutex mutex;
+ Core::HID::EmulatedController* player1_controller;
+ Core::HID::EmulatedController* handheld_controller;
+};