summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-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/hid_core.cpp10
-rw-r--r--src/core/hid/hid_core.h3
-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.cpp51
-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/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.cpp165
-rw-r--r--src/core/hle/service/hid/controllers/npad.h8
-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/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/video_core/gpu.cpp44
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/texture_cache/texture_cache.h9
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp1
-rw-r--r--src/yuzu/debugger/profiler.cpp2
-rw-r--r--src/yuzu/main.cpp17
131 files changed, 2139 insertions, 947 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/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/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/hid_core.cpp b/src/core/hid/hid_core.cpp
index 0c3eb5a62..a1c3bbb57 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -145,6 +145,16 @@ 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();
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index 2fb0f7e19..837f7de49 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -45,6 +45,9 @@ 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();
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..46f27172b
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -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 "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/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 ae56f10cf..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;
@@ -150,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:
@@ -166,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:
@@ -188,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:
@@ -200,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);
@@ -443,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);
@@ -687,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);
@@ -698,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,
@@ -907,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;
@@ -923,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);
@@ -1022,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() {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index de5fa5a64..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);
@@ -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};
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/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/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_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 31adada56..e38cfbc6c 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
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 565b99254..e195b1e98 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,11 @@ 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];
+ if (GetFormatType(dst_info.format) != GetFormatType(dst_image.info.format) ||
+ GetFormatType(src_info.format) != GetFormatType(src_image.info.format)) {
+ // Make sure the images match the expected format.
do {
has_deleted_images = false;
src_id = FindOrInsertImage(src_info, src_addr, RelaxedOptions{});
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/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/main.cpp b/src/yuzu/main.cpp
index 5a9dec8f3..cc84ea11c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1082,14 +1082,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();
+ }
}
}