summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/common/common_funcs.h6
-rw-r--r--src/common/fiber.cpp23
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/misc.cpp44
-rw-r--r--src/core/core.cpp13
-rw-r--r--src/core/cpu_manager.cpp8
-rw-r--r--src/core/crypto/aes_util.cpp2
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp17
-rw-r--r--src/core/hle/kernel/k_thread.cpp50
-rw-r--r--src/core/hle/kernel/k_thread.h24
-rw-r--r--src/core/hle/kernel/kernel.cpp11
-rw-r--r--src/core/hle/kernel/process.cpp5
-rw-r--r--src/core/hle/kernel/svc.cpp5
-rw-r--r--src/core/network/network.cpp173
-rw-r--r--src/core/network/network.h6
-rw-r--r--src/input_common/keyboard.cpp31
-rw-r--r--src/input_common/mouse/mouse_input.cpp47
-rw-r--r--src/input_common/mouse/mouse_input.h17
-rw-r--r--src/input_common/mouse/mouse_poller.cpp20
-rw-r--r--src/input_common/udp/client.cpp26
-rw-r--r--src/input_common/udp/client.h6
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/fibers.cpp28
-rw-r--r--src/tests/core/network/network.cpp28
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h45
-rw-r--r--src/video_core/compatible_formats.cpp49
-rw-r--r--src/video_core/compatible_formats.h5
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/opengl_copy_bgra.comp15
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h5
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp76
-rw-r--r--src/video_core/renderer_opengl/util_shaders.h22
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h5
-rw-r--r--src/video_core/texture_cache/image_base.cpp5
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp2
-rw-r--r--src/video_core/texture_cache/texture_cache.h15
-rw-r--r--src/video_core/texture_cache/util.cpp13
-rw-r--r--src/video_core/texture_cache/util.h9
-rw-r--r--src/yuzu/bootmanager.cpp33
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp25
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp8
-rw-r--r--src/yuzu/configuration/configure_motion_touch.h2
-rw-r--r--src/yuzu/main.cpp28
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp24
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h7
51 files changed, 730 insertions, 296 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1cfd3bbc9..8bd7e5f72 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -27,6 +27,7 @@ if (MSVC)
27 # /Zo - Enhanced debug info for optimized builds 27 # /Zo - Enhanced debug info for optimized builds
28 # /permissive- - Enables stricter C++ standards conformance checks 28 # /permissive- - Enables stricter C++ standards conformance checks
29 # /EHsc - C++-only exception handling semantics 29 # /EHsc - C++-only exception handling semantics
30 # /utf-8 - Set source and execution character sets to UTF-8
30 # /volatile:iso - Use strict standards-compliant volatile semantics. 31 # /volatile:iso - Use strict standards-compliant volatile semantics.
31 # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates 32 # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
32 # /Zc:inline - Let codegen omit inline functions in object files 33 # /Zc:inline - Let codegen omit inline functions in object files
@@ -38,6 +39,7 @@ if (MSVC)
38 /permissive- 39 /permissive-
39 /EHsc 40 /EHsc
40 /std:c++latest 41 /std:c++latest
42 /utf-8
41 /volatile:iso 43 /volatile:iso
42 /Zc:externConstexpr 44 /Zc:externConstexpr
43 /Zc:inline 45 /Zc:inline
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 71b64e32a..4ace2cd33 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -52,9 +52,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
52// Generic function to get last error message. 52// Generic function to get last error message.
53// Call directly after the command or use the error num. 53// Call directly after the command or use the error num.
54// This function might change the error code. 54// This function might change the error code.
55// Defined in Misc.cpp. 55// Defined in misc.cpp.
56[[nodiscard]] std::string GetLastErrorMsg(); 56[[nodiscard]] std::string GetLastErrorMsg();
57 57
58// Like GetLastErrorMsg(), but passing an explicit error code.
59// Defined in misc.cpp.
60[[nodiscard]] std::string NativeErrorToString(int e);
61
58#define DECLARE_ENUM_FLAG_OPERATORS(type) \ 62#define DECLARE_ENUM_FLAG_OPERATORS(type) \
59 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ 63 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
60 using T = std::underlying_type_t<type>; \ 64 using T = std::underlying_type_t<type>; \
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 3c1eefcb7..39532ff58 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -116,16 +116,19 @@ void Fiber::Rewind() {
116 boost::context::detail::jump_fcontext(impl->rewind_context, this); 116 boost::context::detail::jump_fcontext(impl->rewind_context, this);
117} 117}
118 118
119void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { 119void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
120 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 120 to.impl->guard.lock();
121 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 121 to.impl->previous_fiber = weak_from.lock();
122 to->impl->guard.lock(); 122
123 to->impl->previous_fiber = from; 123 auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
124 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); 124
125 ASSERT(from->impl->previous_fiber != nullptr); 125 // "from" might no longer be valid if the thread was killed
126 from->impl->previous_fiber->impl->context = transfer.fctx; 126 if (auto from = weak_from.lock()) {
127 from->impl->previous_fiber->impl->guard.unlock(); 127 ASSERT(from->impl->previous_fiber != nullptr);
128 from->impl->previous_fiber.reset(); 128 from->impl->previous_fiber->impl->context = transfer.fctx;
129 from->impl->previous_fiber->impl->guard.unlock();
130 from->impl->previous_fiber.reset();
131 }
129} 132}
130 133
131std::shared_ptr<Fiber> Fiber::ThreadToFiber() { 134std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
diff --git a/src/common/fiber.h b/src/common/fiber.h
index f7f587f8c..f2a8ff29a 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -41,7 +41,7 @@ public:
41 41
42 /// Yields control from Fiber 'from' to Fiber 'to' 42 /// Yields control from Fiber 'from' to Fiber 'to'
43 /// Fiber 'from' must be the currently running fiber. 43 /// Fiber 'from' must be the currently running fiber.
44 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to); 44 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
45 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); 45 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
46 46
47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); 47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 1d5393597..495385b9e 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -12,27 +12,41 @@
12 12
13#include "common/common_funcs.h" 13#include "common/common_funcs.h"
14 14
15// Generic function to get last error message. 15std::string NativeErrorToString(int e) {
16// Call directly after the command or use the error num.
17// This function might change the error code.
18std::string GetLastErrorMsg() {
19 static constexpr std::size_t buff_size = 255;
20 char err_str[buff_size];
21
22#ifdef _WIN32 16#ifdef _WIN32
23 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 17 LPSTR err_str;
24 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr); 18
25 return std::string(err_str, buff_size); 19 DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
26#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) 20 FORMAT_MESSAGE_IGNORE_INSERTS,
21 nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
22 reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
23 if (!res) {
24 return "(FormatMessageA failed to format error)";
25 }
26 std::string ret(err_str);
27 LocalFree(err_str);
28 return ret;
29#else
30 char err_str[255];
31#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
27 // Thread safe (GNU-specific) 32 // Thread safe (GNU-specific)
28 const char* str = strerror_r(errno, err_str, buff_size); 33 const char* str = strerror_r(e, err_str, sizeof(err_str));
29 return std::string(str); 34 return std::string(str);
30#else 35#else
31 // Thread safe (XSI-compliant) 36 // Thread safe (XSI-compliant)
32 const int success = strerror_r(errno, err_str, buff_size); 37 int second_err = strerror_r(e, err_str, sizeof(err_str));
33 if (success != 0) { 38 if (second_err != 0) {
34 return {}; 39 return "(strerror_r failed to format error)";
35 } 40 }
36 return std::string(err_str); 41 return std::string(err_str);
42#endif // GLIBC etc.
43#endif // _WIN32
44}
45
46std::string GetLastErrorMsg() {
47#ifdef _WIN32
48 return NativeErrorToString(GetLastError());
49#else
50 return NativeErrorToString(errno);
37#endif 51#endif
38} 52}
diff --git a/src/core/core.cpp b/src/core/core.cpp
index de6305e2a..305f56ff1 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -299,28 +299,17 @@ struct System::Impl {
299 gpu_core->WaitIdle(); 299 gpu_core->WaitIdle();
300 } 300 }
301 301
302 // Shutdown emulation session
303 services.reset(); 302 services.reset();
304 service_manager.reset(); 303 service_manager.reset();
305 cheat_engine.reset(); 304 cheat_engine.reset();
306 telemetry_session.reset(); 305 telemetry_session.reset();
307
308 // Close all CPU/threading state
309 cpu_manager.Shutdown(); 306 cpu_manager.Shutdown();
310
311 // Release the Time Manager's resources
312 time_manager.Shutdown(); 307 time_manager.Shutdown();
313
314 // Shutdown kernel and core timing
315 core_timing.Shutdown(); 308 core_timing.Shutdown();
316 kernel.Shutdown();
317
318 // Close app loader
319 app_loader.reset(); 309 app_loader.reset();
320 gpu_core.reset(); 310 gpu_core.reset();
321 perf_stats.reset(); 311 perf_stats.reset();
322 312 kernel.Shutdown();
323 // Clear all applets
324 applet_manager.ClearAll(); 313 applet_manager.ClearAll();
325 314
326 LOG_DEBUG(Core, "Shutdown OK"); 315 LOG_DEBUG(Core, "Shutdown OK");
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 8f04fb8f5..bdb374792 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -148,7 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
148 auto core = kernel.GetCurrentHostThreadID(); 148 auto core = kernel.GetCurrentHostThreadID();
149 auto& scheduler = *kernel.CurrentScheduler(); 149 auto& scheduler = *kernel.CurrentScheduler();
150 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 150 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
151 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); 151 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
152 ASSERT(scheduler.ContextSwitchPending()); 152 ASSERT(scheduler.ContextSwitchPending());
153 ASSERT(core == kernel.GetCurrentHostThreadID()); 153 ASSERT(core == kernel.GetCurrentHostThreadID());
154 scheduler.RescheduleCurrentCore(); 154 scheduler.RescheduleCurrentCore();
@@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
245 auto core = kernel.GetCurrentHostThreadID(); 245 auto core = kernel.GetCurrentHostThreadID();
246 auto& scheduler = *kernel.CurrentScheduler(); 246 auto& scheduler = *kernel.CurrentScheduler();
247 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 247 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
248 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); 248 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
249 ASSERT(scheduler.ContextSwitchPending()); 249 ASSERT(scheduler.ContextSwitchPending());
250 ASSERT(core == kernel.GetCurrentHostThreadID()); 250 ASSERT(core == kernel.GetCurrentHostThreadID());
251 scheduler.RescheduleCurrentCore(); 251 scheduler.RescheduleCurrentCore();
@@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
271 scheduler.Unload(scheduler.GetCurrentThread()); 271 scheduler.Unload(scheduler.GetCurrentThread());
272 272
273 auto& next_scheduler = kernel.Scheduler(current_core); 273 auto& next_scheduler = kernel.Scheduler(current_core);
274 Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); 274 Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
275 } 275 }
276 276
277 // May have changed scheduler 277 // May have changed scheduler
@@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
363 363
364 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 364 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
365 data.is_running = true; 365 data.is_running = true;
366 Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); 366 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
367 data.is_running = false; 367 data.is_running = false;
368 data.is_paused = true; 368 data.is_paused = true;
369 data.exit_barrier->Wait(); 369 data.exit_barrier->Wait();
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 6a9734812..cb7506241 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -105,8 +105,6 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
105 } 105 }
106 } 106 }
107 } 107 }
108
109 mbedtls_cipher_finish(context, nullptr, nullptr);
110} 108}
111 109
112template <typename Key, std::size_t KeySize> 110template <typename Key, std::size_t KeySize>
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index bb5f43b53..e7de48476 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -734,7 +734,7 @@ void KScheduler::ScheduleImpl() {
734 } 734 }
735 guard.unlock(); 735 guard.unlock();
736 736
737 Common::Fiber::YieldTo(*old_context, switch_fiber); 737 Common::Fiber::YieldTo(*old_context, *switch_fiber);
738 /// When a thread wakes up, the scheduler may have changed to other in another core. 738 /// When a thread wakes up, the scheduler may have changed to other in another core.
739 auto& next_scheduler = *system.Kernel().CurrentScheduler(); 739 auto& next_scheduler = *system.Kernel().CurrentScheduler();
740 next_scheduler.SwitchContextStep2(); 740 next_scheduler.SwitchContextStep2();
@@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {
769 break; 769 break;
770 } 770 }
771 } 771 }
772 std::shared_ptr<Common::Fiber>* next_context; 772 auto thread = next_thread ? next_thread : idle_thread;
773 if (next_thread != nullptr) { 773 Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
774 next_context = &next_thread->GetHostContext();
775 } else {
776 next_context = &idle_thread->GetHostContext();
777 }
778 Common::Fiber::YieldTo(switch_fiber, *next_context);
779 } while (!is_switch_pending()); 774 } while (!is_switch_pending());
780 } 775 }
781} 776}
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
800 std::string name = "Idle Thread Id:" + std::to_string(core_id); 795 std::string name = "Idle Thread Id:" + std::to_string(core_id);
801 std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc(); 796 std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
802 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); 797 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
803 auto thread_res = KThread::Create(system, ThreadType::Main, name, 0, 798 auto thread_res = KThread::CreateThread(
804 KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0, 799 system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
805 nullptr, std::move(init_func), init_func_parameter); 800 static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
806 idle_thread = thread_res.Unwrap().get(); 801 idle_thread = thread_res.Unwrap().get();
807} 802}
808 803
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 1661afbd9..e0f53287c 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -995,22 +995,11 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
995 return host_context; 995 return host_context;
996} 996}
997 997
998ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, 998ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
999 std::string name, VAddr entry_point, 999 ThreadType type_flags, std::string name,
1000 u32 priority, u64 arg, s32 processor_id, 1000 VAddr entry_point, u32 priority, u64 arg,
1001 VAddr stack_top, Process* owner_process) { 1001 s32 processor_id, VAddr stack_top,
1002 std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); 1002 Process* owner_process) {
1003 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
1004 return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
1005 owner_process, std::move(init_func), init_func_parameter);
1006}
1007
1008ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
1009 std::string name, VAddr entry_point,
1010 u32 priority, u64 arg, s32 processor_id,
1011 VAddr stack_top, Process* owner_process,
1012 std::function<void(void*)>&& thread_start_func,
1013 void* thread_start_parameter) {
1014 auto& kernel = system.Kernel(); 1003 auto& kernel = system.Kernel();
1015 1004
1016 std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel); 1005 std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
@@ -1027,12 +1016,35 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread
1027 auto& scheduler = kernel.GlobalSchedulerContext(); 1016 auto& scheduler = kernel.GlobalSchedulerContext();
1028 scheduler.AddThread(thread); 1017 scheduler.AddThread(thread);
1029 1018
1030 thread->host_context =
1031 std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
1032
1033 return MakeResult<std::shared_ptr<KThread>>(std::move(thread)); 1019 return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
1034} 1020}
1035 1021
1022ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
1023 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
1024 u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
1025 std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
1026 auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
1027 processor_id, stack_top, owner_process);
1028
1029 if (thread_result.Succeeded()) {
1030 (*thread_result)->host_context =
1031 std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
1032 }
1033
1034 return thread_result;
1035}
1036
1037ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
1038 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
1039 u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
1040 std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
1041
1042 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
1043
1044 return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
1045 stack_top, owner_process, std::move(init_func), init_func_parameter);
1046}
1047
1036KThread* GetCurrentThreadPointer(KernelCore& kernel) { 1048KThread* GetCurrentThreadPointer(KernelCore& kernel) {
1037 return kernel.GetCurrentEmuThread(); 1049 return kernel.GetCurrentEmuThread();
1038} 1050}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c8ac656a4..1c19b23dc 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -116,7 +116,7 @@ public:
116 using WaiterList = boost::intrusive::list<KThread>; 116 using WaiterList = boost::intrusive::list<KThread>;
117 117
118 /** 118 /**
119 * Creates and returns a new thread. The new thread is immediately scheduled 119 * Creates and returns a new thread.
120 * @param system The instance of the whole system 120 * @param system The instance of the whole system
121 * @param name The friendly name desired for the thread 121 * @param name The friendly name desired for the thread
122 * @param entry_point The address at which the thread should start execution 122 * @param entry_point The address at which the thread should start execution
@@ -127,12 +127,12 @@ public:
127 * @param owner_process The parent process for the thread, if null, it's a kernel thread 127 * @param owner_process The parent process for the thread, if null, it's a kernel thread
128 * @return A shared pointer to the newly created thread 128 * @return A shared pointer to the newly created thread
129 */ 129 */
130 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( 130 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
131 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, 131 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
132 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); 132 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
133 133
134 /** 134 /**
135 * Creates and returns a new thread. The new thread is immediately scheduled 135 * Creates and returns a new thread, with a specified entry point.
136 * @param system The instance of the whole system 136 * @param system The instance of the whole system
137 * @param name The friendly name desired for the thread 137 * @param name The friendly name desired for the thread
138 * @param entry_point The address at which the thread should start execution 138 * @param entry_point The address at which the thread should start execution
@@ -145,11 +145,27 @@ public:
145 * @param thread_start_parameter The parameter which will passed to host context on init 145 * @param thread_start_parameter The parameter which will passed to host context on init
146 * @return A shared pointer to the newly created thread 146 * @return A shared pointer to the newly created thread
147 */ 147 */
148 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( 148 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
149 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, 149 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
150 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, 150 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
151 std::function<void(void*)>&& thread_start_func, void* thread_start_parameter); 151 std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
152 152
153 /**
154 * Creates and returns a new thread for the emulated "user" process.
155 * @param system The instance of the whole system
156 * @param name The friendly name desired for the thread
157 * @param entry_point The address at which the thread should start execution
158 * @param priority The thread's priority
159 * @param arg User data to pass to the thread
160 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
161 * @param stack_top The address of the thread's stack top
162 * @param owner_process The parent process for the thread, if null, it's a kernel thread
163 * @return A shared pointer to the newly created thread
164 */
165 [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
166 Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
167 u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
168
153 [[nodiscard]] std::string GetName() const override { 169 [[nodiscard]] std::string GetName() const override {
154 return name; 170 return name;
155 } 171 }
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 331cf3a60..780008b08 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -181,9 +181,9 @@ struct KernelCore::Impl {
181 std::string name = "Suspend Thread Id:" + std::to_string(i); 181 std::string name = "Suspend Thread Id:" + std::to_string(i);
182 std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc(); 182 std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
183 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); 183 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
184 auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0, 184 auto thread_res = KThread::CreateThread(
185 0, 0, static_cast<u32>(i), 0, nullptr, 185 system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
186 std::move(init_func), init_func_parameter); 186 nullptr, std::move(init_func), init_func_parameter);
187 187
188 suspend_threads[i] = std::move(thread_res).Unwrap(); 188 suspend_threads[i] = std::move(thread_res).Unwrap();
189 } 189 }
@@ -221,10 +221,9 @@ struct KernelCore::Impl {
221 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 221 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
222 KThread* GetHostDummyThread() { 222 KThread* GetHostDummyThread() {
223 const thread_local auto thread = 223 const thread_local auto thread =
224 KThread::Create( 224 KThread::CreateThread(
225 system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0, 225 system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
226 KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr, 226 KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
227 []([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
228 .Unwrap(); 227 .Unwrap();
229 return thread.get(); 228 return thread.get();
230 } 229 }
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 73b85d6f9..9d5956ead 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -40,8 +40,9 @@ namespace {
40void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { 40void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
41 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); 41 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); 42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
43 auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, 43 auto thread_res =
44 owner_process.GetIdealCoreId(), stack_top, &owner_process); 44 KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
45 owner_process.GetIdealCoreId(), stack_top, &owner_process);
45 46
46 std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap(); 47 std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
47 48
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index cc8fa6576..326d3b9ec 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1532,8 +1532,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
1532 std::shared_ptr<KThread> thread; 1532 std::shared_ptr<KThread> thread;
1533 { 1533 {
1534 KScopedLightLock lk{process.GetStateLock()}; 1534 KScopedLightLock lk{process.GetStateLock()};
1535 CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority, 1535 CASCADE_RESULT(thread,
1536 arg, core_id, stack_bottom, &process)); 1536 KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
1537 priority, arg, core_id, stack_bottom, &process));
1537 } 1538 }
1538 1539
1539 const auto new_thread_handle = process.GetHandleTable().Create(thread); 1540 const auto new_thread_handle = process.GetHandleTable().Create(thread);
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 681e93468..526bfa110 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -7,6 +7,7 @@
7#include <limits> 7#include <limits>
8#include <utility> 8#include <utility>
9#include <vector> 9#include <vector>
10#include "common/common_funcs.h"
10 11
11#ifdef _WIN32 12#ifdef _WIN32
12#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname 13#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
@@ -90,15 +91,36 @@ LINGER MakeLinger(bool enable, u32 linger_value) {
90 return value; 91 return value;
91} 92}
92 93
93int LastError() {
94 return WSAGetLastError();
95}
96
97bool EnableNonBlock(SOCKET fd, bool enable) { 94bool EnableNonBlock(SOCKET fd, bool enable) {
98 u_long value = enable ? 1 : 0; 95 u_long value = enable ? 1 : 0;
99 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; 96 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
100} 97}
101 98
99Errno TranslateNativeError(int e) {
100 switch (e) {
101 case WSAEBADF:
102 return Errno::BADF;
103 case WSAEINVAL:
104 return Errno::INVAL;
105 case WSAEMFILE:
106 return Errno::MFILE;
107 case WSAENOTCONN:
108 return Errno::NOTCONN;
109 case WSAEWOULDBLOCK:
110 return Errno::AGAIN;
111 case WSAECONNREFUSED:
112 return Errno::CONNREFUSED;
113 case WSAEHOSTUNREACH:
114 return Errno::HOSTUNREACH;
115 case WSAENETDOWN:
116 return Errno::NETDOWN;
117 case WSAENETUNREACH:
118 return Errno::NETUNREACH;
119 default:
120 return Errno::OTHER;
121 }
122}
123
102#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX 124#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX
103 125
104using SOCKET = int; 126using SOCKET = int;
@@ -108,9 +130,6 @@ using ULONG = u64;
108constexpr SOCKET INVALID_SOCKET = -1; 130constexpr SOCKET INVALID_SOCKET = -1;
109constexpr SOCKET SOCKET_ERROR = -1; 131constexpr SOCKET SOCKET_ERROR = -1;
110 132
111constexpr int WSAEWOULDBLOCK = EAGAIN;
112constexpr int WSAENOTCONN = ENOTCONN;
113
114constexpr int SD_RECEIVE = SHUT_RD; 133constexpr int SD_RECEIVE = SHUT_RD;
115constexpr int SD_SEND = SHUT_WR; 134constexpr int SD_SEND = SHUT_WR;
116constexpr int SD_BOTH = SHUT_RDWR; 135constexpr int SD_BOTH = SHUT_RDWR;
@@ -162,10 +181,6 @@ linger MakeLinger(bool enable, u32 linger_value) {
162 return value; 181 return value;
163} 182}
164 183
165int LastError() {
166 return errno;
167}
168
169bool EnableNonBlock(int fd, bool enable) { 184bool EnableNonBlock(int fd, bool enable) {
170 int flags = fcntl(fd, F_GETFD); 185 int flags = fcntl(fd, F_GETFD);
171 if (flags == -1) { 186 if (flags == -1) {
@@ -179,8 +194,43 @@ bool EnableNonBlock(int fd, bool enable) {
179 return fcntl(fd, F_SETFD, flags) == 0; 194 return fcntl(fd, F_SETFD, flags) == 0;
180} 195}
181 196
197Errno TranslateNativeError(int e) {
198 switch (e) {
199 case EBADF:
200 return Errno::BADF;
201 case EINVAL:
202 return Errno::INVAL;
203 case EMFILE:
204 return Errno::MFILE;
205 case ENOTCONN:
206 return Errno::NOTCONN;
207 case EAGAIN:
208 return Errno::AGAIN;
209 case ECONNREFUSED:
210 return Errno::CONNREFUSED;
211 case EHOSTUNREACH:
212 return Errno::HOSTUNREACH;
213 case ENETDOWN:
214 return Errno::NETDOWN;
215 case ENETUNREACH:
216 return Errno::NETUNREACH;
217 default:
218 return Errno::OTHER;
219 }
220}
221
182#endif 222#endif
183 223
224Errno GetAndLogLastError() {
225#ifdef _WIN32
226 int e = WSAGetLastError();
227#else
228 int e = errno;
229#endif
230 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
231 return TranslateNativeError(e);
232}
233
184int TranslateDomain(Domain domain) { 234int TranslateDomain(Domain domain) {
185 switch (domain) { 235 switch (domain) {
186 case Domain::INET: 236 case Domain::INET:
@@ -290,9 +340,7 @@ Errno SetSockOpt(SOCKET fd, int option, T value) {
290 if (result != SOCKET_ERROR) { 340 if (result != SOCKET_ERROR) {
291 return Errno::SUCCESS; 341 return Errno::SUCCESS;
292 } 342 }
293 const int ec = LastError(); 343 return GetAndLogLastError();
294 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
295 return Errno::SUCCESS;
296} 344}
297 345
298} // Anonymous namespace 346} // Anonymous namespace
@@ -308,14 +356,12 @@ NetworkInstance::~NetworkInstance() {
308std::pair<IPv4Address, Errno> GetHostIPv4Address() { 356std::pair<IPv4Address, Errno> GetHostIPv4Address() {
309 std::array<char, 256> name{}; 357 std::array<char, 256> name{};
310 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { 358 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
311 UNIMPLEMENTED_MSG("Unhandled gethostname error"); 359 return {IPv4Address{}, GetAndLogLastError()};
312 return {IPv4Address{}, Errno::SUCCESS};
313 } 360 }
314 361
315 hostent* const ent = gethostbyname(name.data()); 362 hostent* const ent = gethostbyname(name.data());
316 if (!ent) { 363 if (!ent) {
317 UNIMPLEMENTED_MSG("Unhandled gethostbyname error"); 364 return {IPv4Address{}, GetAndLogLastError()};
318 return {IPv4Address{}, Errno::SUCCESS};
319 } 365 }
320 if (ent->h_addr_list == nullptr) { 366 if (ent->h_addr_list == nullptr) {
321 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); 367 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
@@ -359,9 +405,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
359 405
360 ASSERT(result == SOCKET_ERROR); 406 ASSERT(result == SOCKET_ERROR);
361 407
362 const int ec = LastError(); 408 return {-1, GetAndLogLastError()};
363 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
364 return {-1, Errno::SUCCESS};
365} 409}
366 410
367Socket::~Socket() { 411Socket::~Socket() {
@@ -380,9 +424,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
380 return Errno::SUCCESS; 424 return Errno::SUCCESS;
381 } 425 }
382 426
383 const int ec = LastError(); 427 return GetAndLogLastError();
384 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
385 return Errno::SUCCESS;
386} 428}
387 429
388std::pair<Socket::AcceptResult, Errno> Socket::Accept() { 430std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
@@ -391,9 +433,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
391 const SOCKET new_socket = accept(fd, &addr, &addrlen); 433 const SOCKET new_socket = accept(fd, &addr, &addrlen);
392 434
393 if (new_socket == INVALID_SOCKET) { 435 if (new_socket == INVALID_SOCKET) {
394 const int ec = LastError(); 436 return {AcceptResult{}, GetAndLogLastError()};
395 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
396 return {AcceptResult{}, Errno::SUCCESS};
397 } 437 }
398 438
399 AcceptResult result; 439 AcceptResult result;
@@ -412,23 +452,14 @@ Errno Socket::Connect(SockAddrIn addr_in) {
412 return Errno::SUCCESS; 452 return Errno::SUCCESS;
413 } 453 }
414 454
415 switch (const int ec = LastError()) { 455 return GetAndLogLastError();
416 case WSAEWOULDBLOCK:
417 LOG_DEBUG(Service, "EAGAIN generated");
418 return Errno::AGAIN;
419 default:
420 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
421 return Errno::SUCCESS;
422 }
423} 456}
424 457
425std::pair<SockAddrIn, Errno> Socket::GetPeerName() { 458std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
426 sockaddr addr; 459 sockaddr addr;
427 socklen_t addrlen = sizeof(addr); 460 socklen_t addrlen = sizeof(addr);
428 if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { 461 if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) {
429 const int ec = LastError(); 462 return {SockAddrIn{}, GetAndLogLastError()};
430 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
431 return {SockAddrIn{}, Errno::SUCCESS};
432 } 463 }
433 464
434 ASSERT(addrlen == sizeof(sockaddr_in)); 465 ASSERT(addrlen == sizeof(sockaddr_in));
@@ -439,9 +470,7 @@ std::pair<SockAddrIn, Errno> Socket::GetSockName() {
439 sockaddr addr; 470 sockaddr addr;
440 socklen_t addrlen = sizeof(addr); 471 socklen_t addrlen = sizeof(addr);
441 if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { 472 if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) {
442 const int ec = LastError(); 473 return {SockAddrIn{}, GetAndLogLastError()};
443 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
444 return {SockAddrIn{}, Errno::SUCCESS};
445 } 474 }
446 475
447 ASSERT(addrlen == sizeof(sockaddr_in)); 476 ASSERT(addrlen == sizeof(sockaddr_in));
@@ -454,9 +483,7 @@ Errno Socket::Bind(SockAddrIn addr) {
454 return Errno::SUCCESS; 483 return Errno::SUCCESS;
455 } 484 }
456 485
457 const int ec = LastError(); 486 return GetAndLogLastError();
458 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
459 return Errno::SUCCESS;
460} 487}
461 488
462Errno Socket::Listen(s32 backlog) { 489Errno Socket::Listen(s32 backlog) {
@@ -464,9 +491,7 @@ Errno Socket::Listen(s32 backlog) {
464 return Errno::SUCCESS; 491 return Errno::SUCCESS;
465 } 492 }
466 493
467 const int ec = LastError(); 494 return GetAndLogLastError();
468 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
469 return Errno::SUCCESS;
470} 495}
471 496
472Errno Socket::Shutdown(ShutdownHow how) { 497Errno Socket::Shutdown(ShutdownHow how) {
@@ -489,14 +514,7 @@ Errno Socket::Shutdown(ShutdownHow how) {
489 return Errno::SUCCESS; 514 return Errno::SUCCESS;
490 } 515 }
491 516
492 switch (const int ec = LastError()) { 517 return GetAndLogLastError();
493 case WSAENOTCONN:
494 LOG_ERROR(Service, "ENOTCONN generated");
495 return Errno::NOTCONN;
496 default:
497 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
498 return Errno::SUCCESS;
499 }
500} 518}
501 519
502std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { 520std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
@@ -509,17 +527,7 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
509 return {static_cast<s32>(result), Errno::SUCCESS}; 527 return {static_cast<s32>(result), Errno::SUCCESS};
510 } 528 }
511 529
512 switch (const int ec = LastError()) { 530 return {-1, GetAndLogLastError()};
513 case WSAEWOULDBLOCK:
514 LOG_DEBUG(Service, "EAGAIN generated");
515 return {-1, Errno::AGAIN};
516 case WSAENOTCONN:
517 LOG_ERROR(Service, "ENOTCONN generated");
518 return {-1, Errno::NOTCONN};
519 default:
520 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
521 return {0, Errno::SUCCESS};
522 }
523} 531}
524 532
525std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { 533std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
@@ -541,17 +549,7 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
541 return {static_cast<s32>(result), Errno::SUCCESS}; 549 return {static_cast<s32>(result), Errno::SUCCESS};
542 } 550 }
543 551
544 switch (const int ec = LastError()) { 552 return {-1, GetAndLogLastError()};
545 case WSAEWOULDBLOCK:
546 LOG_DEBUG(Service, "EAGAIN generated");
547 return {-1, Errno::AGAIN};
548 case WSAENOTCONN:
549 LOG_ERROR(Service, "ENOTCONN generated");
550 return {-1, Errno::NOTCONN};
551 default:
552 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
553 return {-1, Errno::SUCCESS};
554 }
555} 553}
556 554
557std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { 555std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
@@ -564,18 +562,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
564 return {static_cast<s32>(result), Errno::SUCCESS}; 562 return {static_cast<s32>(result), Errno::SUCCESS};
565 } 563 }
566 564
567 const int ec = LastError(); 565 return {-1, GetAndLogLastError()};
568 switch (ec) {
569 case WSAEWOULDBLOCK:
570 LOG_DEBUG(Service, "EAGAIN generated");
571 return {-1, Errno::AGAIN};
572 case WSAENOTCONN:
573 LOG_ERROR(Service, "ENOTCONN generated");
574 return {-1, Errno::NOTCONN};
575 default:
576 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
577 return {-1, Errno::SUCCESS};
578 }
579} 566}
580 567
581std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, 568std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
@@ -597,9 +584,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
597 return {static_cast<s32>(result), Errno::SUCCESS}; 584 return {static_cast<s32>(result), Errno::SUCCESS};
598 } 585 }
599 586
600 const int ec = LastError(); 587 return {-1, GetAndLogLastError()};
601 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
602 return {-1, Errno::SUCCESS};
603} 588}
604 589
605Errno Socket::Close() { 590Errno Socket::Close() {
@@ -642,9 +627,7 @@ Errno Socket::SetNonBlock(bool enable) {
642 if (EnableNonBlock(fd, enable)) { 627 if (EnableNonBlock(fd, enable)) {
643 return Errno::SUCCESS; 628 return Errno::SUCCESS;
644 } 629 }
645 const int ec = LastError(); 630 return GetAndLogLastError();
646 UNREACHABLE_MSG("Unhandled host socket error={}", ec);
647 return Errno::SUCCESS;
648} 631}
649 632
650bool Socket::IsOpened() const { 633bool Socket::IsOpened() const {
diff --git a/src/core/network/network.h b/src/core/network/network.h
index 76b2821f2..bd30f1899 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <utility> 8#include <utility>
9 9
10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
12namespace Network { 13namespace Network {
@@ -21,6 +22,11 @@ enum class Errno {
21 MFILE, 22 MFILE,
22 NOTCONN, 23 NOTCONN,
23 AGAIN, 24 AGAIN,
25 CONNREFUSED,
26 HOSTUNREACH,
27 NETDOWN,
28 NETUNREACH,
29 OTHER,
24}; 30};
25 31
26/// Address families 32/// Address families
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
index 24a6f7a33..c467ff4c5 100644
--- a/src/input_common/keyboard.cpp
+++ b/src/input_common/keyboard.cpp
@@ -12,20 +12,39 @@ namespace InputCommon {
12 12
13class KeyButton final : public Input::ButtonDevice { 13class KeyButton final : public Input::ButtonDevice {
14public: 14public:
15 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) 15 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_)
16 : key_button_list(std::move(key_button_list_)) {} 16 : key_button_list(std::move(key_button_list_)), toggle(toggle_) {}
17 17
18 ~KeyButton() override; 18 ~KeyButton() override;
19 19
20 bool GetStatus() const override { 20 bool GetStatus() const override {
21 if (toggle) {
22 return toggled_status.load(std::memory_order_relaxed);
23 }
21 return status.load(); 24 return status.load();
22 } 25 }
23 26
27 void ToggleButton() {
28 if (lock) {
29 return;
30 }
31 lock = true;
32 const bool old_toggle_status = toggled_status.load();
33 toggled_status.store(!old_toggle_status);
34 }
35
36 void UnlockButton() {
37 lock = false;
38 }
39
24 friend class KeyButtonList; 40 friend class KeyButtonList;
25 41
26private: 42private:
27 std::shared_ptr<KeyButtonList> key_button_list; 43 std::shared_ptr<KeyButtonList> key_button_list;
28 std::atomic<bool> status{false}; 44 std::atomic<bool> status{false};
45 std::atomic<bool> toggled_status{false};
46 bool lock{false};
47 const bool toggle;
29}; 48};
30 49
31struct KeyButtonPair { 50struct KeyButtonPair {
@@ -51,6 +70,11 @@ public:
51 for (const KeyButtonPair& pair : list) { 70 for (const KeyButtonPair& pair : list) {
52 if (pair.key_code == key_code) { 71 if (pair.key_code == key_code) {
53 pair.key_button->status.store(pressed); 72 pair.key_button->status.store(pressed);
73 if (pressed) {
74 pair.key_button->ToggleButton();
75 } else {
76 pair.key_button->UnlockButton();
77 }
54 } 78 }
55 } 79 }
56 } 80 }
@@ -75,7 +99,8 @@ KeyButton::~KeyButton() {
75 99
76std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) { 100std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
77 const int key_code = params.Get("code", 0); 101 const int key_code = params.Get("code", 0);
78 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); 102 const bool toggle = params.Get("toggle", false);
103 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle);
79 key_button_list->AddKeyButton(key_code, button.get()); 104 key_button_list->AddKeyButton(key_code, button.get());
80 return button; 105 return button;
81} 106}
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
index b864d26f2..329e416c7 100644
--- a/src/input_common/mouse/mouse_input.cpp
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -59,7 +59,7 @@ void Mouse::UpdateYuzuSettings() {
59 }); 59 });
60} 60}
61 61
62void Mouse::PressButton(int x, int y, int button_) { 62void Mouse::PressButton(int x, int y, MouseButton button_) {
63 const auto button_index = static_cast<std::size_t>(button_); 63 const auto button_index = static_cast<std::size_t>(button_);
64 if (button_index >= mouse_info.size()) { 64 if (button_index >= mouse_info.size()) {
65 return; 65 return;
@@ -67,7 +67,7 @@ void Mouse::PressButton(int x, int y, int button_) {
67 67
68 const auto button = 1U << button_index; 68 const auto button = 1U << button_index;
69 buttons |= static_cast<u16>(button); 69 buttons |= static_cast<u16>(button);
70 last_button = static_cast<MouseButton>(button_index); 70 last_button = button_;
71 71
72 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y); 72 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
73 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y); 73 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
@@ -129,7 +129,7 @@ void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
129 } 129 }
130} 130}
131 131
132void Mouse::ReleaseButton(int button_) { 132void Mouse::ReleaseButton(MouseButton button_) {
133 const auto button_index = static_cast<std::size_t>(button_); 133 const auto button_index = static_cast<std::size_t>(button_);
134 if (button_index >= mouse_info.size()) { 134 if (button_index >= mouse_info.size()) {
135 return; 135 return;
@@ -152,11 +152,52 @@ void Mouse::BeginConfiguration() {
152 152
153void Mouse::EndConfiguration() { 153void Mouse::EndConfiguration() {
154 buttons = 0; 154 buttons = 0;
155 for (MouseInfo& info : mouse_info) {
156 info.tilt_speed = 0;
157 info.data.pressed = false;
158 info.data.axis = {0, 0};
159 }
155 last_button = MouseButton::Undefined; 160 last_button = MouseButton::Undefined;
156 mouse_queue.Clear(); 161 mouse_queue.Clear();
157 configuring = false; 162 configuring = false;
158} 163}
159 164
165bool Mouse::ToggleButton(std::size_t button_) {
166 if (button_ >= mouse_info.size()) {
167 return false;
168 }
169 const auto button = 1U << button_;
170 const bool button_state = (toggle_buttons & button) != 0;
171 const bool button_lock = (lock_buttons & button) != 0;
172
173 if (button_lock) {
174 return button_state;
175 }
176
177 lock_buttons |= static_cast<u16>(button);
178
179 if (button_state) {
180 toggle_buttons &= static_cast<u16>(0xFF - button);
181 } else {
182 toggle_buttons |= static_cast<u16>(button);
183 }
184
185 return !button_state;
186}
187
188bool Mouse::UnlockButton(std::size_t button_) {
189 if (button_ >= mouse_info.size()) {
190 return false;
191 }
192
193 const auto button = 1U << button_;
194 const bool button_state = (toggle_buttons & button) != 0;
195
196 lock_buttons &= static_cast<u16>(0xFF - button);
197
198 return button_state;
199}
200
160Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() { 201Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
161 return mouse_queue; 202 return mouse_queue;
162} 203}
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
index 46aa676c1..750d9b011 100644
--- a/src/input_common/mouse/mouse_input.h
+++ b/src/input_common/mouse/mouse_input.h
@@ -18,10 +18,12 @@ namespace MouseInput {
18 18
19enum class MouseButton { 19enum class MouseButton {
20 Left, 20 Left,
21 Wheel,
22 Right, 21 Right,
23 Forward, 22 Wheel,
24 Backward, 23 Backward,
24 Forward,
25 Task,
26 Extra,
25 Undefined, 27 Undefined,
26}; 28};
27 29
@@ -51,7 +53,7 @@ public:
51 * @param y the y-coordinate of the cursor 53 * @param y the y-coordinate of the cursor
52 * @param button_ the button pressed 54 * @param button_ the button pressed
53 */ 55 */
54 void PressButton(int x, int y, int button_); 56 void PressButton(int x, int y, MouseButton button_);
55 57
56 /** 58 /**
57 * Signals that mouse has moved. 59 * Signals that mouse has moved.
@@ -65,7 +67,10 @@ public:
65 /** 67 /**
66 * Signals that a motion sensor tilt has ended. 68 * Signals that a motion sensor tilt has ended.
67 */ 69 */
68 void ReleaseButton(int button_); 70 void ReleaseButton(MouseButton button_);
71
72 [[nodiscard]] bool ToggleButton(std::size_t button_);
73 [[nodiscard]] bool UnlockButton(std::size_t button_);
69 74
70 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue(); 75 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
71 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const; 76 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
@@ -92,9 +97,11 @@ private:
92 }; 97 };
93 98
94 u16 buttons{}; 99 u16 buttons{};
100 u16 toggle_buttons{};
101 u16 lock_buttons{};
95 std::thread update_thread; 102 std::thread update_thread;
96 MouseButton last_button{MouseButton::Undefined}; 103 MouseButton last_button{MouseButton::Undefined};
97 std::array<MouseInfo, 5> mouse_info; 104 std::array<MouseInfo, 7> mouse_info;
98 Common::SPSCQueue<MouseStatus> mouse_queue; 105 Common::SPSCQueue<MouseStatus> mouse_queue;
99 bool configuring{false}; 106 bool configuring{false};
100 bool update_thread_running{true}; 107 bool update_thread_running{true};
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
index bb56787ee..0e1db54fb 100644
--- a/src/input_common/mouse/mouse_poller.cpp
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -14,16 +14,25 @@ namespace InputCommon {
14 14
15class MouseButton final : public Input::ButtonDevice { 15class MouseButton final : public Input::ButtonDevice {
16public: 16public:
17 explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_) 17 explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_)
18 : button(button_), mouse_input(mouse_input_) {} 18 : button(button_), toggle(toggle_), mouse_input(mouse_input_) {}
19 19
20 bool GetStatus() const override { 20 bool GetStatus() const override {
21 return mouse_input->GetMouseState(button).pressed; 21 const bool button_state = mouse_input->GetMouseState(button).pressed;
22 if (!toggle) {
23 return button_state;
24 }
25
26 if (button_state) {
27 return mouse_input->ToggleButton(button);
28 }
29 return mouse_input->UnlockButton(button);
22 } 30 }
23 31
24private: 32private:
25 const u32 button; 33 const u32 button;
26 const MouseInput::Mouse* mouse_input; 34 const bool toggle;
35 MouseInput::Mouse* mouse_input;
27}; 36};
28 37
29MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) 38MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
@@ -32,8 +41,9 @@ MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_
32std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create( 41std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
33 const Common::ParamPackage& params) { 42 const Common::ParamPackage& params) {
34 const auto button_id = params.Get("button", 0); 43 const auto button_id = params.Get("button", 0);
44 const auto toggle = params.Get("toggle", false);
35 45
36 return std::make_unique<MouseButton>(button_id, mouse_input.get()); 46 return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get());
37} 47}
38 48
39Common::ParamPackage MouseButtonFactory::GetNextInput() const { 49Common::ParamPackage MouseButtonFactory::GetNextInput() const {
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index c4afa4174..df73f9ff7 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -5,6 +5,7 @@
5#include <chrono> 5#include <chrono>
6#include <cstring> 6#include <cstring>
7#include <functional> 7#include <functional>
8#include <random>
8#include <thread> 9#include <thread>
9#include <boost/asio.hpp> 10#include <boost/asio.hpp>
10#include "common/logging/log.h" 11#include "common/logging/log.h"
@@ -26,10 +27,10 @@ class Socket {
26public: 27public:
27 using clock = std::chrono::system_clock; 28 using clock = std::chrono::system_clock;
28 29
29 explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_, 30 explicit Socket(const std::string& host, u16 port, std::size_t pad_index_,
30 SocketCallback callback_) 31 SocketCallback callback_)
31 : callback(std::move(callback_)), timer(io_service), 32 : callback(std::move(callback_)), timer(io_service),
32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_), 33 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()),
33 pad_index(pad_index_) { 34 pad_index(pad_index_) {
34 boost::system::error_code ec{}; 35 boost::system::error_code ec{};
35 auto ipv4 = boost::asio::ip::make_address_v4(host, ec); 36 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
@@ -63,6 +64,11 @@ public:
63 } 64 }
64 65
65private: 66private:
67 u32 GenerateRandomClientId() const {
68 std::random_device device;
69 return device();
70 }
71
66 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) { 72 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { 73 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
68 switch (*type) { 74 switch (*type) {
@@ -115,7 +121,7 @@ private:
115 boost::asio::basic_waitable_timer<clock> timer; 121 boost::asio::basic_waitable_timer<clock> timer;
116 udp::socket socket; 122 udp::socket socket;
117 123
118 u32 client_id{}; 124 const u32 client_id;
119 std::size_t pad_index{}; 125 std::size_t pad_index{};
120 126
121 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); 127 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
@@ -203,7 +209,7 @@ void Client::ReloadSockets() {
203 LOG_ERROR(Input, "Duplicated UDP servers found"); 209 LOG_ERROR(Input, "Duplicated UDP servers found");
204 continue; 210 continue;
205 } 211 }
206 StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872); 212 StartCommunication(client++, udp_input_address, udp_input_port, pad);
207 } 213 }
208 } 214 }
209} 215}
@@ -277,7 +283,7 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {
277} 283}
278 284
279void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, 285void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
280 std::size_t pad_index, u32 client_id) { 286 std::size_t pad_index) {
281 SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, 287 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
282 [this](Response::PortInfo info) { OnPortInfo(info); }, 288 [this](Response::PortInfo info) { OnPortInfo(info); },
283 [this, client](Response::PadData data) { OnPadData(data, client); }}; 289 [this, client](Response::PadData data) { OnPadData(data, client); }};
@@ -287,7 +293,7 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16
287 clients[client].port = port; 293 clients[client].port = port;
288 clients[client].pad_index = pad_index; 294 clients[client].pad_index = pad_index;
289 clients[client].active = 0; 295 clients[client].active = 0;
290 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); 296 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback);
291 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; 297 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
292 // Set motion parameters 298 // Set motion parameters
293 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode 299 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
@@ -416,7 +422,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
416 return pad_queue; 422 return pad_queue;
417} 423}
418 424
419void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, 425void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
420 const std::function<void()>& success_callback, 426 const std::function<void()>& success_callback,
421 const std::function<void()>& failure_callback) { 427 const std::function<void()>& failure_callback) {
422 std::thread([=] { 428 std::thread([=] {
@@ -426,7 +432,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
426 .port_info = [](Response::PortInfo) {}, 432 .port_info = [](Response::PortInfo) {},
427 .pad_data = [&](Response::PadData) { success_event.Set(); }, 433 .pad_data = [&](Response::PadData) { success_event.Set(); },
428 }; 434 };
429 Socket socket{host, port, pad_index, client_id, std::move(callback)}; 435 Socket socket{host, port, pad_index, std::move(callback)};
430 std::thread worker_thread{SocketLoop, &socket}; 436 std::thread worker_thread{SocketLoop, &socket};
431 const bool result = success_event.WaitFor(std::chrono::seconds(5)); 437 const bool result = success_event.WaitFor(std::chrono::seconds(5));
432 socket.Stop(); 438 socket.Stop();
@@ -440,7 +446,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
440} 446}
441 447
442CalibrationConfigurationJob::CalibrationConfigurationJob( 448CalibrationConfigurationJob::CalibrationConfigurationJob(
443 const std::string& host, u16 port, std::size_t pad_index, u32 client_id, 449 const std::string& host, u16 port, std::size_t pad_index,
444 std::function<void(Status)> status_callback, 450 std::function<void(Status)> status_callback,
445 std::function<void(u16, u16, u16, u16)> data_callback) { 451 std::function<void(u16, u16, u16, u16)> data_callback) {
446 452
@@ -485,7 +491,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
485 complete_event.Set(); 491 complete_event.Set();
486 } 492 }
487 }}; 493 }};
488 Socket socket{host, port, pad_index, client_id, std::move(callback)}; 494 Socket socket{host, port, pad_index, std::move(callback)};
489 std::thread worker_thread{SocketLoop, &socket}; 495 std::thread worker_thread{SocketLoop, &socket};
490 complete_event.Wait(); 496 complete_event.Wait();
491 socket.Stop(); 497 socket.Stop();
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index a523f6124..e9e438e88 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -126,7 +126,7 @@ private:
126 void OnPortInfo(Response::PortInfo); 126 void OnPortInfo(Response::PortInfo);
127 void OnPadData(Response::PadData, std::size_t client); 127 void OnPadData(Response::PadData, std::size_t client);
128 void StartCommunication(std::size_t client, const std::string& host, u16 port, 128 void StartCommunication(std::size_t client, const std::string& host, u16 port,
129 std::size_t pad_index, u32 client_id); 129 std::size_t pad_index);
130 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, 130 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
131 const Common::Vec3<float>& gyro); 131 const Common::Vec3<float>& gyro);
132 132
@@ -165,7 +165,7 @@ public:
165 * @param data_callback Called when calibration data is ready 165 * @param data_callback Called when calibration data is ready
166 */ 166 */
167 explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index, 167 explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
168 u32 client_id, std::function<void(Status)> status_callback, 168 std::function<void(Status)> status_callback,
169 std::function<void(u16, u16, u16, u16)> data_callback); 169 std::function<void(u16, u16, u16, u16)> data_callback);
170 ~CalibrationConfigurationJob(); 170 ~CalibrationConfigurationJob();
171 void Stop(); 171 void Stop();
@@ -174,7 +174,7 @@ private:
174 Common::Event complete_event; 174 Common::Event complete_event;
175}; 175};
176 176
177void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, 177void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
178 const std::function<void()>& success_callback, 178 const std::function<void()>& success_callback,
179 const std::function<void()>& failure_callback); 179 const std::function<void()>& failure_callback);
180 180
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 4ea0076e9..d875c4fee 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -5,6 +5,7 @@ add_executable(tests
5 common/param_package.cpp 5 common/param_package.cpp
6 common/ring_buffer.cpp 6 common/ring_buffer.cpp
7 core/core_timing.cpp 7 core/core_timing.cpp
8 core/network/network.cpp
8 tests.cpp 9 tests.cpp
9 video_core/buffer_base.cpp 10 video_core/buffer_base.cpp
10) 11)
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index d94492fc6..751cbe196 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -67,7 +67,7 @@ void TestControl1::DoWork() {
67 value++; 67 value++;
68 } 68 }
69 results[id] = value; 69 results[id] = value;
70 Fiber::YieldTo(work_fibers[id], thread_fibers[id]); 70 Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
71} 71}
72 72
73void TestControl1::ExecuteThread(u32 id) { 73void TestControl1::ExecuteThread(u32 id) {
@@ -76,7 +76,7 @@ void TestControl1::ExecuteThread(u32 id) {
76 thread_fibers[id] = thread_fiber; 76 thread_fibers[id] = thread_fiber;
77 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); 77 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
78 items[id] = rand() % 256; 78 items[id] = rand() % 256;
79 Fiber::YieldTo(thread_fibers[id], work_fibers[id]); 79 Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
80 thread_fibers[id]->Exit(); 80 thread_fibers[id]->Exit();
81} 81}
82 82
@@ -117,11 +117,11 @@ public:
117 for (u32 i = 0; i < 12000; i++) { 117 for (u32 i = 0; i < 12000; i++) {
118 value1 += i; 118 value1 += i;
119 } 119 }
120 Fiber::YieldTo(fiber1, fiber3); 120 Fiber::YieldTo(fiber1, *fiber3);
121 const u32 id = thread_ids.Get(); 121 const u32 id = thread_ids.Get();
122 assert1 = id == 1; 122 assert1 = id == 1;
123 value2 += 5000; 123 value2 += 5000;
124 Fiber::YieldTo(fiber1, thread_fibers[id]); 124 Fiber::YieldTo(fiber1, *thread_fibers[id]);
125 } 125 }
126 126
127 void DoWork2() { 127 void DoWork2() {
@@ -129,7 +129,7 @@ public:
129 ; 129 ;
130 value2 = 2000; 130 value2 = 2000;
131 trap = false; 131 trap = false;
132 Fiber::YieldTo(fiber2, fiber1); 132 Fiber::YieldTo(fiber2, *fiber1);
133 assert3 = false; 133 assert3 = false;
134 } 134 }
135 135
@@ -137,19 +137,19 @@ public:
137 const u32 id = thread_ids.Get(); 137 const u32 id = thread_ids.Get();
138 assert2 = id == 0; 138 assert2 = id == 0;
139 value1 += 1000; 139 value1 += 1000;
140 Fiber::YieldTo(fiber3, thread_fibers[id]); 140 Fiber::YieldTo(fiber3, *thread_fibers[id]);
141 } 141 }
142 142
143 void ExecuteThread(u32 id); 143 void ExecuteThread(u32 id);
144 144
145 void CallFiber1() { 145 void CallFiber1() {
146 const u32 id = thread_ids.Get(); 146 const u32 id = thread_ids.Get();
147 Fiber::YieldTo(thread_fibers[id], fiber1); 147 Fiber::YieldTo(thread_fibers[id], *fiber1);
148 } 148 }
149 149
150 void CallFiber2() { 150 void CallFiber2() {
151 const u32 id = thread_ids.Get(); 151 const u32 id = thread_ids.Get();
152 Fiber::YieldTo(thread_fibers[id], fiber2); 152 Fiber::YieldTo(thread_fibers[id], *fiber2);
153 } 153 }
154 154
155 void Exit(); 155 void Exit();
@@ -241,23 +241,23 @@ public:
241 241
242 void DoWork1() { 242 void DoWork1() {
243 value1 += 1; 243 value1 += 1;
244 Fiber::YieldTo(fiber1, fiber2); 244 Fiber::YieldTo(fiber1, *fiber2);
245 const u32 id = thread_ids.Get(); 245 const u32 id = thread_ids.Get();
246 value3 += 1; 246 value3 += 1;
247 Fiber::YieldTo(fiber1, thread_fibers[id]); 247 Fiber::YieldTo(fiber1, *thread_fibers[id]);
248 } 248 }
249 249
250 void DoWork2() { 250 void DoWork2() {
251 value2 += 1; 251 value2 += 1;
252 const u32 id = thread_ids.Get(); 252 const u32 id = thread_ids.Get();
253 Fiber::YieldTo(fiber2, thread_fibers[id]); 253 Fiber::YieldTo(fiber2, *thread_fibers[id]);
254 } 254 }
255 255
256 void ExecuteThread(u32 id); 256 void ExecuteThread(u32 id);
257 257
258 void CallFiber1() { 258 void CallFiber1() {
259 const u32 id = thread_ids.Get(); 259 const u32 id = thread_ids.Get();
260 Fiber::YieldTo(thread_fibers[id], fiber1); 260 Fiber::YieldTo(thread_fibers[id], *fiber1);
261 } 261 }
262 262
263 void Exit(); 263 void Exit();
@@ -332,7 +332,7 @@ public:
332 332
333 void Execute() { 333 void Execute() {
334 thread_fiber = Fiber::ThreadToFiber(); 334 thread_fiber = Fiber::ThreadToFiber();
335 Fiber::YieldTo(thread_fiber, fiber1); 335 Fiber::YieldTo(thread_fiber, *fiber1);
336 thread_fiber->Exit(); 336 thread_fiber->Exit();
337 } 337 }
338 338
@@ -340,7 +340,7 @@ public:
340 fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); 340 fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);
341 if (rewinded) { 341 if (rewinded) {
342 goal_reached = true; 342 goal_reached = true;
343 Fiber::YieldTo(fiber1, thread_fiber); 343 Fiber::YieldTo(fiber1, *thread_fiber);
344 } 344 }
345 rewinded = true; 345 rewinded = true;
346 fiber1->Rewind(); 346 fiber1->Rewind();
diff --git a/src/tests/core/network/network.cpp b/src/tests/core/network/network.cpp
new file mode 100644
index 000000000..b21ad8911
--- /dev/null
+++ b/src/tests/core/network/network.cpp
@@ -0,0 +1,28 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch2/catch.hpp>
6
7#include "core/network/network.h"
8#include "core/network/sockets.h"
9
10TEST_CASE("Network::Errors", "[core]") {
11 Network::NetworkInstance network_instance; // initialize network
12
13 Network::Socket socks[2];
14 for (Network::Socket& sock : socks) {
15 REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
16 Network::Protocol::TCP) == Network::Errno::SUCCESS);
17 }
18
19 Network::SockAddrIn addr{
20 Network::Domain::INET,
21 {127, 0, 0, 1},
22 1, // hopefully nobody running this test has something listening on port 1
23 };
24 REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
25
26 std::vector<u8> message{1, 2, 3, 4};
27 REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
28}
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 2a6844ab1..4de1e37e5 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -9,6 +9,7 @@
9#include <deque> 9#include <deque>
10#include <memory> 10#include <memory>
11#include <mutex> 11#include <mutex>
12#include <numeric>
12#include <span> 13#include <span>
13#include <unordered_map> 14#include <unordered_map>
14#include <vector> 15#include <vector>
@@ -91,7 +92,7 @@ class BufferCache {
91 }; 92 };
92 93
93public: 94public:
94 static constexpr u32 SKIP_CACHE_SIZE = 4096; 95 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;
95 96
96 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, 97 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
97 Tegra::Engines::Maxwell3D& maxwell3d_, 98 Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -240,9 +241,9 @@ private:
240 template <bool insert> 241 template <bool insert>
241 void ChangeRegister(BufferId buffer_id); 242 void ChangeRegister(BufferId buffer_id);
242 243
243 void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); 244 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
244 245
245 void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); 246 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
246 247
247 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 248 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
248 std::span<BufferCopy> copies); 249 std::span<BufferCopy> copies);
@@ -297,6 +298,11 @@ private:
297 298
298 std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{}; 299 std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{};
299 300
301 std::array<u32, 16> uniform_cache_hits{};
302 std::array<u32, 16> uniform_cache_shots{};
303
304 u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
305
300 bool has_deleted_buffers = false; 306 bool has_deleted_buffers = false;
301 307
302 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty> 308 std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
@@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
328 334
329template <class P> 335template <class P>
330void BufferCache<P>::TickFrame() { 336void BufferCache<P>::TickFrame() {
337 // Calculate hits and shots and move hit bits to the right
338 const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end());
339 const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end());
340 std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1,
341 uniform_cache_hits.begin() + 1);
342 std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1,
343 uniform_cache_shots.begin() + 1);
344 uniform_cache_hits[0] = 0;
345 uniform_cache_shots[0] = 0;
346
347 const bool skip_preferred = hits * 256 < shots * 251;
348 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
349
331 delayed_destruction_ring.Tick(); 350 delayed_destruction_ring.Tick();
332} 351}
333 352
@@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
671 const VAddr cpu_addr = binding.cpu_addr; 690 const VAddr cpu_addr = binding.cpu_addr;
672 const u32 size = binding.size; 691 const u32 size = binding.size;
673 Buffer& buffer = slot_buffers[binding.buffer_id]; 692 Buffer& buffer = slot_buffers[binding.buffer_id];
674 if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) { 693 if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) {
675 if constexpr (IS_OPENGL) { 694 if constexpr (IS_OPENGL) {
676 if (runtime.HasFastBufferSubData()) { 695 if (runtime.HasFastBufferSubData()) {
677 // Fast path for Nvidia 696 // Fast path for Nvidia
@@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
692 return; 711 return;
693 } 712 }
694 // Classic cached path 713 // Classic cached path
695 SynchronizeBuffer(buffer, cpu_addr, size); 714 const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size);
715 if (sync_cached) {
716 ++uniform_cache_hits[0];
717 }
718 ++uniform_cache_shots[0];
719
696 if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) { 720 if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) {
697 // Skip binding if it's not needed and if the bound buffer is not the fast version 721 // Skip binding if it's not needed and if the bound buffer is not the fast version
698 // This exists to avoid instances where the fast buffer is bound and a GPU write happens 722 // This exists to avoid instances where the fast buffer is bound and a GPU write happens
@@ -1106,15 +1130,15 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1106} 1130}
1107 1131
1108template <class P> 1132template <class P>
1109void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { 1133bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
1110 if (buffer.CpuAddr() == 0) { 1134 if (buffer.CpuAddr() == 0) {
1111 return; 1135 return true;
1112 } 1136 }
1113 SynchronizeBufferImpl(buffer, cpu_addr, size); 1137 return SynchronizeBufferImpl(buffer, cpu_addr, size);
1114} 1138}
1115 1139
1116template <class P> 1140template <class P>
1117void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { 1141bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
1118 boost::container::small_vector<BufferCopy, 4> copies; 1142 boost::container::small_vector<BufferCopy, 4> copies;
1119 u64 total_size_bytes = 0; 1143 u64 total_size_bytes = 0;
1120 u64 largest_copy = 0; 1144 u64 largest_copy = 0;
@@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
1128 largest_copy = std::max(largest_copy, range_size); 1152 largest_copy = std::max(largest_copy, range_size);
1129 }); 1153 });
1130 if (total_size_bytes == 0) { 1154 if (total_size_bytes == 0) {
1131 return; 1155 return true;
1132 } 1156 }
1133 const std::span<BufferCopy> copies_span(copies.data(), copies.size()); 1157 const std::span<BufferCopy> copies_span(copies.data(), copies.size());
1134 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); 1158 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
1159 return false;
1135} 1160}
1136 1161
1137template <class P> 1162template <class P>
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
index acf2668dc..8317d0636 100644
--- a/src/video_core/compatible_formats.cpp
+++ b/src/video_core/compatible_formats.cpp
@@ -48,6 +48,15 @@ constexpr std::array VIEW_CLASS_32_BITS{
48 PixelFormat::A2B10G10R10_UINT, 48 PixelFormat::A2B10G10R10_UINT,
49}; 49};
50 50
51constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
52 PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT,
53 PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
54 PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
55 PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
56 PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
57 PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
58};
59
51// TODO: How should we handle 24 bits? 60// TODO: How should we handle 24 bits?
52 61
53constexpr std::array VIEW_CLASS_16_BITS{ 62constexpr std::array VIEW_CLASS_16_BITS{
@@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {
205 EnableRange(view, VIEW_CLASS_128_BITS); 214 EnableRange(view, VIEW_CLASS_128_BITS);
206 EnableRange(view, VIEW_CLASS_96_BITS); 215 EnableRange(view, VIEW_CLASS_96_BITS);
207 EnableRange(view, VIEW_CLASS_64_BITS); 216 EnableRange(view, VIEW_CLASS_64_BITS);
208 EnableRange(view, VIEW_CLASS_32_BITS);
209 EnableRange(view, VIEW_CLASS_16_BITS); 217 EnableRange(view, VIEW_CLASS_16_BITS);
210 EnableRange(view, VIEW_CLASS_8_BITS); 218 EnableRange(view, VIEW_CLASS_8_BITS);
211 EnableRange(view, VIEW_CLASS_RGTC1_RED); 219 EnableRange(view, VIEW_CLASS_RGTC1_RED);
@@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {
231 EnableRange(copy, COPY_CLASS_64_BITS); 239 EnableRange(copy, COPY_CLASS_64_BITS);
232 return copy; 240 return copy;
233} 241}
242
243constexpr Table MakeNativeBgrViewTable() {
244 Table copy = MakeViewTable();
245 EnableRange(copy, VIEW_CLASS_32_BITS);
246 return copy;
247}
248
249constexpr Table MakeNonNativeBgrViewTable() {
250 Table copy = MakeViewTable();
251 EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR);
252 return copy;
253}
254
255constexpr Table MakeNativeBgrCopyTable() {
256 Table copy = MakeCopyTable();
257 EnableRange(copy, VIEW_CLASS_32_BITS);
258 return copy;
259}
260
261constexpr Table MakeNonNativeBgrCopyTable() {
262 Table copy = MakeCopyTable();
263 EnableRange(copy, VIEW_CLASS_32_BITS);
264 return copy;
265}
234} // Anonymous namespace 266} // Anonymous namespace
235 267
236bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) { 268bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
269 bool native_bgr) {
237 if (broken_views) { 270 if (broken_views) {
238 // If format views are broken, only accept formats that are identical. 271 // If format views are broken, only accept formats that are identical.
239 return format_a == format_b; 272 return format_a == format_b;
240 } 273 }
241 static constexpr Table TABLE = MakeViewTable(); 274 static constexpr Table BGR_TABLE = MakeNativeBgrViewTable();
242 return IsSupported(TABLE, format_a, format_b); 275 static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable();
276 return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
243} 277}
244 278
245bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) { 279bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) {
246 static constexpr Table TABLE = MakeCopyTable(); 280 static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable();
247 return IsSupported(TABLE, format_a, format_b); 281 static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable();
282 return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
248} 283}
249 284
250} // namespace VideoCore::Surface 285} // namespace VideoCore::Surface
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h
index 9a0522988..55745e042 100644
--- a/src/video_core/compatible_formats.h
+++ b/src/video_core/compatible_formats.h
@@ -8,8 +8,9 @@
8 8
9namespace VideoCore::Surface { 9namespace VideoCore::Surface {
10 10
11bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views); 11bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
12 bool native_bgr);
12 13
13bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); 14bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);
14 15
15} // namespace VideoCore::Surface 16} // namespace VideoCore::Surface
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 970120acc..3494318ca 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SHADER_FILES
5 convert_float_to_depth.frag 5 convert_float_to_depth.frag
6 full_screen_triangle.vert 6 full_screen_triangle.vert
7 opengl_copy_bc4.comp 7 opengl_copy_bc4.comp
8 opengl_copy_bgra.comp
8 opengl_present.frag 9 opengl_present.frag
9 opengl_present.vert 10 opengl_present.vert
10 pitch_unswizzle.comp 11 pitch_unswizzle.comp
diff --git a/src/video_core/host_shaders/opengl_copy_bgra.comp b/src/video_core/host_shaders/opengl_copy_bgra.comp
new file mode 100644
index 000000000..2571a4abf
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_copy_bgra.comp
@@ -0,0 +1,15 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 430 core
6
7layout (local_size_x = 4, local_size_y = 4) in;
8
9layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input;
10layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output;
11
12void main() {
13 vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID));
14 imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra);
15}
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 6da3906a4..c225d1fc9 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -73,7 +73,8 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_)
73 for (auto& stage_uniforms : fast_uniforms) { 73 for (auto& stage_uniforms : fast_uniforms) {
74 for (OGLBuffer& buffer : stage_uniforms) { 74 for (OGLBuffer& buffer : stage_uniforms) {
75 buffer.Create(); 75 buffer.Create();
76 glNamedBufferData(buffer.handle, BufferCache::SKIP_CACHE_SIZE, nullptr, GL_STREAM_DRAW); 76 glNamedBufferData(buffer.handle, BufferCache::DEFAULT_SKIP_CACHE_SIZE, nullptr,
77 GL_STREAM_DRAW);
77 } 78 }
78 } 79 }
79 for (auto& stage_uniforms : copy_uniforms) { 80 for (auto& stage_uniforms : copy_uniforms) {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 12434db67..e028677e9 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -96,7 +96,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
96 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT 96 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT
97 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT 97 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT
98 {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM 98 {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM
99 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM 99 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
100 {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT 100 {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT
101 {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT 101 {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT
102 {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT 102 {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT
@@ -125,7 +125,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
125 {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM 125 {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM
126 {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM 126 {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM
127 {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM 127 {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM
128 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM 128 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB
129 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB 129 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB
130 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB 130 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB
131 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 131 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
@@ -396,6 +396,17 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
396 } 396 }
397} 397}
398 398
399[[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) {
400 switch (format) {
401 case PixelFormat::B5G6R5_UNORM:
402 case PixelFormat::B8G8R8A8_UNORM:
403 case PixelFormat::B8G8R8A8_SRGB:
404 return true;
405 default:
406 return false;
407 }
408}
409
399} // Anonymous namespace 410} // Anonymous namespace
400 411
401ImageBufferMap::~ImageBufferMap() { 412ImageBufferMap::~ImageBufferMap() {
@@ -512,6 +523,9 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) {
512 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { 523 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
513 return false; 524 return false;
514 } 525 }
526 if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
527 return false;
528 }
515 return true; 529 return true;
516} 530}
517 531
@@ -520,6 +534,8 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
520 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { 534 if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
521 ASSERT(src.info.type == ImageType::e3D); 535 ASSERT(src.info.type == ImageType::e3D);
522 util_shaders.CopyBC4(dst, src, copies); 536 util_shaders.CopyBC4(dst, src, copies);
537 } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
538 util_shaders.CopyBGR(dst, src, copies);
523 } else { 539 } else {
524 UNREACHABLE(); 540 UNREACHABLE();
525 } 541 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index a6172f009..3fbaa102f 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -86,6 +86,11 @@ public:
86 86
87 FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; 87 FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
88 88
89 bool HasNativeBgr() const noexcept {
90 // OpenGL does not have native support for the BGR internal format
91 return false;
92 }
93
89 bool HasBrokenTextureViewFormats() const noexcept { 94 bool HasBrokenTextureViewFormats() const noexcept {
90 return has_broken_texture_view_formats; 95 return has_broken_texture_view_formats;
91 } 96 }
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 31ec68505..2fe4799bc 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -14,6 +14,7 @@
14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" 14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" 15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
16#include "video_core/host_shaders/opengl_copy_bc4_comp.h" 16#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
17#include "video_core/host_shaders/opengl_copy_bgra_comp.h"
17#include "video_core/host_shaders/pitch_unswizzle_comp.h" 18#include "video_core/host_shaders/pitch_unswizzle_comp.h"
18#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
19#include "video_core/renderer_opengl/gl_shader_manager.h" 20#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -48,6 +49,11 @@ OGLProgram MakeProgram(std::string_view source) {
48 return program; 49 return program;
49} 50}
50 51
52size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
53 return static_cast<size_t>(copy.extent.width * copy.extent.height *
54 copy.src_subresource.num_layers);
55}
56
51} // Anonymous namespace 57} // Anonymous namespace
52 58
53UtilShaders::UtilShaders(ProgramManager& program_manager_) 59UtilShaders::UtilShaders(ProgramManager& program_manager_)
@@ -55,6 +61,7 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
55 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), 61 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
56 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), 62 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
57 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), 63 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
64 copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),
58 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { 65 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
59 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); 66 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
60 swizzle_table_buffer.Create(); 67 swizzle_table_buffer.Create();
@@ -205,6 +212,43 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
205 program_manager.RestoreGuestCompute(); 212 program_manager.RestoreGuestCompute();
206} 213}
207 214
215void UtilShaders::CopyBGR(Image& dst_image, Image& src_image,
216 std::span<const VideoCommon::ImageCopy> copies) {
217 static constexpr GLuint BINDING_INPUT_IMAGE = 0;
218 static constexpr GLuint BINDING_OUTPUT_IMAGE = 1;
219 static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0};
220 const u32 bytes_per_block = BytesPerBlock(dst_image.info.format);
221 switch (bytes_per_block) {
222 case 2:
223 // BGR565 copy
224 for (const ImageCopy& copy : copies) {
225 ASSERT(copy.src_offset == zero_offset);
226 ASSERT(copy.dst_offset == zero_offset);
227 bgr_copy_pass.Execute(dst_image, src_image, copy);
228 }
229 break;
230 case 4: {
231 // BGRA8 copy
232 program_manager.BindHostCompute(copy_bgra_program.handle);
233 constexpr GLenum FORMAT = GL_RGBA8;
234 for (const ImageCopy& copy : copies) {
235 ASSERT(copy.src_offset == zero_offset);
236 ASSERT(copy.dst_offset == zero_offset);
237 glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
238 copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT);
239 glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
240 copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT);
241 glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
242 }
243 program_manager.RestoreGuestCompute();
244 break;
245 }
246 default:
247 UNREACHABLE();
248 break;
249 }
250}
251
208GLenum StoreFormat(u32 bytes_per_block) { 252GLenum StoreFormat(u32 bytes_per_block) {
209 switch (bytes_per_block) { 253 switch (bytes_per_block) {
210 case 1: 254 case 1:
@@ -222,4 +266,36 @@ GLenum StoreFormat(u32 bytes_per_block) {
222 return GL_R8UI; 266 return GL_R8UI;
223} 267}
224 268
269void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image,
270 const ImageCopy& copy) {
271 if (CopyBufferCreationNeeded(copy)) {
272 CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565);
273 }
274 // Copy from source to PBO
275 glPixelStorei(GL_PACK_ALIGNMENT, 1);
276 glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width);
277 glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle);
278 glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
279 copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
280 static_cast<GLsizei>(bgr16_pbo_size), nullptr);
281
282 // Copy from PBO to destination in reverse order
283 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
284 glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width);
285 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle);
286 glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
287 copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV,
288 nullptr);
289}
290
291bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) {
292 return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16);
293}
294
295void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) {
296 bgr16_pbo.Create();
297 bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16);
298 glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY);
299}
300
225} // namespace OpenGL 301} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h
index 7b1d16b09..93b009743 100644
--- a/src/video_core/renderer_opengl/util_shaders.h
+++ b/src/video_core/renderer_opengl/util_shaders.h
@@ -19,6 +19,22 @@ class ProgramManager;
19 19
20struct ImageBufferMap; 20struct ImageBufferMap;
21 21
22class Bgr565CopyPass {
23public:
24 Bgr565CopyPass() = default;
25 ~Bgr565CopyPass() = default;
26
27 void Execute(const Image& dst_image, const Image& src_image,
28 const VideoCommon::ImageCopy& copy);
29
30private:
31 [[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy);
32 void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format);
33
34 OGLBuffer bgr16_pbo;
35 size_t bgr16_pbo_size{};
36};
37
22class UtilShaders { 38class UtilShaders {
23public: 39public:
24 explicit UtilShaders(ProgramManager& program_manager); 40 explicit UtilShaders(ProgramManager& program_manager);
@@ -36,6 +52,9 @@ public:
36 void CopyBC4(Image& dst_image, Image& src_image, 52 void CopyBC4(Image& dst_image, Image& src_image,
37 std::span<const VideoCommon::ImageCopy> copies); 53 std::span<const VideoCommon::ImageCopy> copies);
38 54
55 void CopyBGR(Image& dst_image, Image& src_image,
56 std::span<const VideoCommon::ImageCopy> copies);
57
39private: 58private:
40 ProgramManager& program_manager; 59 ProgramManager& program_manager;
41 60
@@ -44,7 +63,10 @@ private:
44 OGLProgram block_linear_unswizzle_2d_program; 63 OGLProgram block_linear_unswizzle_2d_program;
45 OGLProgram block_linear_unswizzle_3d_program; 64 OGLProgram block_linear_unswizzle_3d_program;
46 OGLProgram pitch_unswizzle_program; 65 OGLProgram pitch_unswizzle_program;
66 OGLProgram copy_bgra_program;
47 OGLProgram copy_bc4_program; 67 OGLProgram copy_bc4_program;
68
69 Bgr565CopyPass bgr_copy_pass;
48}; 70};
49 71
50GLenum StoreFormat(u32 bytes_per_block); 72GLenum StoreFormat(u32 bytes_per_block);
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp
index a99df9323..d8e92ac0e 100644
--- a/src/video_core/renderer_vulkan/vk_command_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp
@@ -10,7 +10,7 @@
10 10
11namespace Vulkan { 11namespace Vulkan {
12 12
13constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000; 13constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;
14 14
15struct CommandPool::Pool { 15struct CommandPool::Pool {
16 vk::CommandPool handle; 16 vk::CommandPool handle;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index b08c23459..3aee27ce0 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -93,6 +93,11 @@ struct TextureCacheRuntime {
93 // No known Vulkan driver has broken image views 93 // No known Vulkan driver has broken image views
94 return false; 94 return false;
95 } 95 }
96
97 bool HasNativeBgr() const noexcept {
98 // All known Vulkan drivers can natively handle BGR textures
99 return true;
100 }
96}; 101};
97 102
98class Image : public VideoCommon::ImageBase { 103class Image : public VideoCommon::ImageBase {
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp
index 959b3f115..9914926b3 100644
--- a/src/video_core/texture_cache/image_base.cpp
+++ b/src/video_core/texture_cache/image_base.cpp
@@ -120,9 +120,10 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
120 if (lhs.info.type == ImageType::Linear) { 120 if (lhs.info.type == ImageType::Linear) {
121 base = SubresourceBase{.level = 0, .layer = 0}; 121 base = SubresourceBase{.level = 0, .layer = 0};
122 } else { 122 } else {
123 // We are passing relaxed formats as an option, having broken views or not won't matter 123 // We are passing relaxed formats as an option, having broken views/bgr or not won't matter
124 static constexpr bool broken_views = false; 124 static constexpr bool broken_views = false;
125 base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views); 125 static constexpr bool native_bgr = true;
126 base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
126 } 127 }
127 if (!base) { 128 if (!base) {
128 LOG_ERROR(HW_GPU, "Image alias should have been flipped"); 129 LOG_ERROR(HW_GPU, "Image alias should have been flipped");
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index 18f72e508..f89a40b4c 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
24 .height = std::max(image_info.size.height >> range.base.level, 1u), 24 .height = std::max(image_info.size.height >> range.base.level, 1u),
25 .depth = std::max(image_info.size.depth >> range.base.level, 1u), 25 .depth = std::max(image_info.size.depth >> range.base.level, 1u),
26 } { 26 } {
27 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false), 27 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
28 "Image view format {} is incompatible with image format {}", info.format, 28 "Image view format {} is incompatible with image format {}", info.format,
29 image_info.format); 29 image_info.format);
30 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 30 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index b1da69971..98e33c3a0 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -876,6 +876,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
876 return ImageId{}; 876 return ImageId{};
877 } 877 }
878 const bool broken_views = runtime.HasBrokenTextureViewFormats(); 878 const bool broken_views = runtime.HasBrokenTextureViewFormats();
879 const bool native_bgr = runtime.HasNativeBgr();
879 ImageId image_id; 880 ImageId image_id;
880 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 881 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
881 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { 882 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
@@ -885,11 +886,12 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
885 if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && 886 if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
886 existing.pitch == info.pitch && 887 existing.pitch == info.pitch &&
887 IsPitchLinearSameSize(existing, info, strict_size) && 888 IsPitchLinearSameSize(existing, info, strict_size) &&
888 IsViewCompatible(existing.format, info.format, broken_views)) { 889 IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
889 image_id = existing_image_id; 890 image_id = existing_image_id;
890 return true; 891 return true;
891 } 892 }
892 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) { 893 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
894 native_bgr)) {
893 image_id = existing_image_id; 895 image_id = existing_image_id;
894 return true; 896 return true;
895 } 897 }
@@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
920 ImageInfo new_info = info; 922 ImageInfo new_info = info;
921 const size_t size_bytes = CalculateGuestSizeInBytes(new_info); 923 const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
922 const bool broken_views = runtime.HasBrokenTextureViewFormats(); 924 const bool broken_views = runtime.HasBrokenTextureViewFormats();
925 const bool native_bgr = runtime.HasNativeBgr();
923 std::vector<ImageId> overlap_ids; 926 std::vector<ImageId> overlap_ids;
924 std::vector<ImageId> left_aliased_ids; 927 std::vector<ImageId> left_aliased_ids;
925 std::vector<ImageId> right_aliased_ids; 928 std::vector<ImageId> right_aliased_ids;
@@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
935 return; 938 return;
936 } 939 }
937 static constexpr bool strict_size = true; 940 static constexpr bool strict_size = true;
938 const std::optional<OverlapResult> solution = 941 const std::optional<OverlapResult> solution = ResolveOverlap(
939 ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views); 942 new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
940 if (solution) { 943 if (solution) {
941 gpu_addr = solution->gpu_addr; 944 gpu_addr = solution->gpu_addr;
942 cpu_addr = solution->cpu_addr; 945 cpu_addr = solution->cpu_addr;
@@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
946 } 949 }
947 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; 950 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
948 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); 951 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
949 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) { 952 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
950 left_aliased_ids.push_back(overlap_id); 953 left_aliased_ids.push_back(overlap_id);
951 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, 954 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
952 broken_views)) { 955 broken_views, native_bgr)) {
953 right_aliased_ids.push_back(overlap_id); 956 right_aliased_ids.push_back(overlap_id);
954 } 957 }
955 }); 958 });
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index a0bc1f7b6..2c42d1449 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -1035,13 +1035,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri
1035 1035
1036std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, 1036std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,
1037 VAddr cpu_addr, const ImageBase& overlap, 1037 VAddr cpu_addr, const ImageBase& overlap,
1038 bool strict_size, bool broken_views) { 1038 bool strict_size, bool broken_views, bool native_bgr) {
1039 ASSERT(new_info.type != ImageType::Linear); 1039 ASSERT(new_info.type != ImageType::Linear);
1040 ASSERT(overlap.info.type != ImageType::Linear); 1040 ASSERT(overlap.info.type != ImageType::Linear);
1041 if (!IsLayerStrideCompatible(new_info, overlap.info)) { 1041 if (!IsLayerStrideCompatible(new_info, overlap.info)) {
1042 return std::nullopt; 1042 return std::nullopt;
1043 } 1043 }
1044 if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) { 1044 if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) {
1045 return std::nullopt; 1045 return std::nullopt;
1046 } 1046 }
1047 if (gpu_addr == overlap.gpu_addr) { 1047 if (gpu_addr == overlap.gpu_addr) {
@@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {
1085 1085
1086std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, 1086std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,
1087 GPUVAddr candidate_addr, RelaxedOptions options, 1087 GPUVAddr candidate_addr, RelaxedOptions options,
1088 bool broken_views) { 1088 bool broken_views, bool native_bgr) {
1089 const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); 1089 const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
1090 if (!base) { 1090 if (!base) {
1091 return std::nullopt; 1091 return std::nullopt;
1092 } 1092 }
1093 const ImageInfo& existing = image.info; 1093 const ImageInfo& existing = image.info;
1094 if (False(options & RelaxedOptions::Format)) { 1094 if (False(options & RelaxedOptions::Format)) {
1095 if (!IsViewCompatible(existing.format, candidate.format, broken_views)) { 1095 if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {
1096 return std::nullopt; 1096 return std::nullopt;
1097 } 1097 }
1098 } 1098 }
@@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
1129} 1129}
1130 1130
1131bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, 1131bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr,
1132 RelaxedOptions options, bool broken_views) { 1132 RelaxedOptions options, bool broken_views, bool native_bgr) {
1133 return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value(); 1133 return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr)
1134 .has_value();
1134} 1135}
1135 1136
1136void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 1137void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 52a9207d6..4d0072867 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -87,7 +87,8 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
87[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, 87[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
88 GPUVAddr gpu_addr, VAddr cpu_addr, 88 GPUVAddr gpu_addr, VAddr cpu_addr,
89 const ImageBase& overlap, 89 const ImageBase& overlap,
90 bool strict_size, bool broken_views); 90 bool strict_size, bool broken_views,
91 bool native_bgr);
91 92
92[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); 93[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
93 94
@@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
95 const ImageBase& image, 96 const ImageBase& image,
96 GPUVAddr candidate_addr, 97 GPUVAddr candidate_addr,
97 RelaxedOptions options, 98 RelaxedOptions options,
98 bool broken_views); 99 bool broken_views, bool native_bgr);
99 100
100[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, 101[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
101 GPUVAddr candidate_addr, RelaxedOptions options, 102 GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views,
102 bool broken_views); 103 bool native_bgr);
103 104
104void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 105void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
105 const ImageBase* src); 106 const ImageBase* src);
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1c61d419d..15c09e0ad 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -376,11 +376,34 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
376} 376}
377 377
378void GRenderWindow::keyPressEvent(QKeyEvent* event) { 378void GRenderWindow::keyPressEvent(QKeyEvent* event) {
379 input_subsystem->GetKeyboard()->PressKey(event->key()); 379 if (!event->isAutoRepeat()) {
380 input_subsystem->GetKeyboard()->PressKey(event->key());
381 }
380} 382}
381 383
382void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 384void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
383 input_subsystem->GetKeyboard()->ReleaseKey(event->key()); 385 if (!event->isAutoRepeat()) {
386 input_subsystem->GetKeyboard()->ReleaseKey(event->key());
387 }
388}
389
390MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
391 switch (button) {
392 case Qt::LeftButton:
393 return MouseInput::MouseButton::Left;
394 case Qt::RightButton:
395 return MouseInput::MouseButton::Right;
396 case Qt::MiddleButton:
397 return MouseInput::MouseButton::Wheel;
398 case Qt::BackButton:
399 return MouseInput::MouseButton::Backward;
400 case Qt::ForwardButton:
401 return MouseInput::MouseButton::Forward;
402 case Qt::TaskButton:
403 return MouseInput::MouseButton::Task;
404 default:
405 return MouseInput::MouseButton::Extra;
406 }
384} 407}
385 408
386void GRenderWindow::mousePressEvent(QMouseEvent* event) { 409void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -391,7 +414,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
391 414
392 auto pos = event->pos(); 415 auto pos = event->pos();
393 const auto [x, y] = ScaleTouch(pos); 416 const auto [x, y] = ScaleTouch(pos);
394 input_subsystem->GetMouse()->PressButton(x, y, event->button()); 417 const auto button = QtButtonToMouseButton(event->button());
418 input_subsystem->GetMouse()->PressButton(x, y, button);
395 419
396 if (event->button() == Qt::LeftButton) { 420 if (event->button() == Qt::LeftButton) {
397 this->TouchPressed(x, y, 0); 421 this->TouchPressed(x, y, 0);
@@ -425,7 +449,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
425 return; 449 return;
426 } 450 }
427 451
428 input_subsystem->GetMouse()->ReleaseButton(event->button()); 452 const auto button = QtButtonToMouseButton(event->button());
453 input_subsystem->GetMouse()->ReleaseButton(button);
429 454
430 if (event->button() == Qt::LeftButton) { 455 if (event->button() == Qt::LeftButton) {
431 this->TouchReleased(0); 456 this->TouchReleased(0);
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index b5ec7de07..acfe2bc8c 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -28,6 +28,10 @@ namespace InputCommon {
28class InputSubsystem; 28class InputSubsystem;
29} 29}
30 30
31namespace MouseInput {
32enum class MouseButton;
33}
34
31namespace VideoCore { 35namespace VideoCore {
32enum class LoadCallbackStage; 36enum class LoadCallbackStage;
33} 37}
@@ -149,6 +153,9 @@ public:
149 void keyPressEvent(QKeyEvent* event) override; 153 void keyPressEvent(QKeyEvent* event) override;
150 void keyReleaseEvent(QKeyEvent* event) override; 154 void keyReleaseEvent(QKeyEvent* event) override;
151 155
156 /// Converts a Qt mouse button into MouseInput mouse button
157 static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
158
152 void mousePressEvent(QMouseEvent* event) override; 159 void mousePressEvent(QMouseEvent* event) override;
153 void mouseMoveEvent(QMouseEvent* event) override; 160 void mouseMoveEvent(QMouseEvent* event) override;
154 void mouseReleaseEvent(QMouseEvent* event) override; 161 void mouseReleaseEvent(QMouseEvent* event) override;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index b319d69fc..1bac57bb2 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -235,7 +235,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{
235 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, 235 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
236 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, 236 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
237 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, 237 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
238 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}}, 238 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
239 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, 239 {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
240 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, 240 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
241}}; 241}};
@@ -508,7 +508,7 @@ void Config::ReadControlValues() {
508 508
509 Settings::values.emulate_analog_keyboard = 509 Settings::values.emulate_analog_keyboard =
510 ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); 510 ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool();
511 Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool(); 511 Settings::values.mouse_panning = false;
512 Settings::values.mouse_panning_sensitivity = 512 Settings::values.mouse_panning_sensitivity =
513 ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); 513 ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat();
514 514
@@ -1182,7 +1182,6 @@ void Config::SaveControlValues() {
1182 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); 1182 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
1183 WriteSetting(QStringLiteral("emulate_analog_keyboard"), 1183 WriteSetting(QStringLiteral("emulate_analog_keyboard"),
1184 Settings::values.emulate_analog_keyboard, false); 1184 Settings::values.emulate_analog_keyboard, false);
1185 WriteSetting(QStringLiteral("mouse_panning"), Settings::values.mouse_panning, false);
1186 WriteSetting(QStringLiteral("mouse_panning_sensitivity"), 1185 WriteSetting(QStringLiteral("mouse_panning_sensitivity"),
1187 Settings::values.mouse_panning_sensitivity, 1.0f); 1186 Settings::values.mouse_panning_sensitivity, 1.0f);
1188 qt_config->endGroup(); 1187 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 21d0d3449..c9318c562 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -21,6 +21,7 @@
21#include "input_common/mouse/mouse_poller.h" 21#include "input_common/mouse/mouse_poller.h"
22#include "input_common/udp/udp.h" 22#include "input_common/udp/udp.h"
23#include "ui_configure_input_player.h" 23#include "ui_configure_input_player.h"
24#include "yuzu/bootmanager.h"
24#include "yuzu/configuration/config.h" 25#include "yuzu/configuration/config.h"
25#include "yuzu/configuration/configure_input_player.h" 26#include "yuzu/configuration/configure_input_player.h"
26#include "yuzu/configuration/configure_input_player_widget.h" 27#include "yuzu/configuration/configure_input_player_widget.h"
@@ -104,7 +105,9 @@ QString ButtonToText(const Common::ParamPackage& param) {
104 } 105 }
105 106
106 if (param.Get("engine", "") == "keyboard") { 107 if (param.Get("engine", "") == "keyboard") {
107 return GetKeyName(param.Get("code", 0)); 108 const QString button_str = GetKeyName(param.Get("code", 0));
109 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
110 return QObject::tr("%1%2").arg(toggle, button_str);
108 } 111 }
109 112
110 if (param.Get("engine", "") == "gcpad") { 113 if (param.Get("engine", "") == "gcpad") {
@@ -156,7 +159,8 @@ QString ButtonToText(const Common::ParamPackage& param) {
156 if (param.Get("engine", "") == "mouse") { 159 if (param.Get("engine", "") == "mouse") {
157 if (param.Has("button")) { 160 if (param.Has("button")) {
158 const QString button_str = QString::number(int(param.Get("button", 0))); 161 const QString button_str = QString::number(int(param.Get("button", 0)));
159 return QObject::tr("Click %1").arg(button_str); 162 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
163 return QObject::tr("%1Click %2").arg(toggle, button_str);
160 } 164 }
161 return GetKeyName(param.Get("code", 0)); 165 return GetKeyName(param.Get("code", 0));
162 } 166 }
@@ -300,6 +304,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
300 buttons_param[button_id].Clear(); 304 buttons_param[button_id].Clear();
301 button_map[button_id]->setText(tr("[not set]")); 305 button_map[button_id]->setText(tr("[not set]"));
302 }); 306 });
307 context_menu.addAction(tr("Toggle button"), [&] {
308 const bool toggle_value = !buttons_param[button_id].Get("toggle", false);
309 buttons_param[button_id].Set("toggle", toggle_value);
310 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
311 });
303 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); 312 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
304 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); 313 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
305 }); 314 });
@@ -412,6 +421,15 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
412 analogs_param[analog_id].Set("modifier", ""); 421 analogs_param[analog_id].Set("modifier", "");
413 analog_map_modifier_button[analog_id]->setText(tr("[not set]")); 422 analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
414 }); 423 });
424 context_menu.addAction(tr("Toggle button"), [&] {
425 Common::ParamPackage modifier_param =
426 Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")};
427 const bool toggle_value = !modifier_param.Get("toggle", false);
428 modifier_param.Set("toggle", toggle_value);
429 analogs_param[analog_id].Set("modifier", modifier_param.Serialize());
430 analog_map_modifier_button[analog_id]->setText(
431 ButtonToText(modifier_param));
432 });
415 context_menu.exec( 433 context_menu.exec(
416 analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); 434 analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
417 }); 435 });
@@ -1345,7 +1363,8 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1345 return; 1363 return;
1346 } 1364 }
1347 1365
1348 input_subsystem->GetMouse()->PressButton(0, 0, event->button()); 1366 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
1367 input_subsystem->GetMouse()->PressButton(0, 0, button);
1349} 1368}
1350 1369
1351void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1370void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 1f2b792e4..52fdf7265 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -24,7 +24,7 @@
24 24
25CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, 25CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
26 const std::string& host, u16 port, 26 const std::string& host, u16 port,
27 u8 pad_index, u16 client_id) 27 u8 pad_index)
28 : QDialog(parent) { 28 : QDialog(parent) {
29 layout = new QVBoxLayout; 29 layout = new QVBoxLayout;
30 status_label = new QLabel(tr("Communicating with the server...")); 30 status_label = new QLabel(tr("Communicating with the server..."));
@@ -41,7 +41,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
41 41
42 using namespace InputCommon::CemuhookUDP; 42 using namespace InputCommon::CemuhookUDP;
43 job = std::make_unique<CalibrationConfigurationJob>( 43 job = std::make_unique<CalibrationConfigurationJob>(
44 host, port, pad_index, client_id, 44 host, port, pad_index,
45 [this](CalibrationConfigurationJob::Status status) { 45 [this](CalibrationConfigurationJob::Status status) {
46 QString text; 46 QString text;
47 switch (status) { 47 switch (status) {
@@ -218,7 +218,6 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() {
218 udp_test_in_progress = true; 218 udp_test_in_progress = true;
219 InputCommon::CemuhookUDP::TestCommunication( 219 InputCommon::CemuhookUDP::TestCommunication(
220 ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), 0, 220 ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), 0,
221 24872,
222 [this] { 221 [this] {
223 LOG_INFO(Frontend, "UDP input test success"); 222 LOG_INFO(Frontend, "UDP input test success");
224 QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); 223 QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true));
@@ -233,8 +232,7 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() {
233 ui->touch_calibration_config->setEnabled(false); 232 ui->touch_calibration_config->setEnabled(false);
234 ui->touch_calibration_config->setText(tr("Configuring")); 233 ui->touch_calibration_config->setText(tr("Configuring"));
235 CalibrationConfigurationDialog dialog(this, ui->udp_server->text().toStdString(), 234 CalibrationConfigurationDialog dialog(this, ui->udp_server->text().toStdString(),
236 static_cast<u16>(ui->udp_port->text().toUInt()), 0, 235 static_cast<u16>(ui->udp_port->text().toUInt()), 0);
237 24872);
238 dialog.exec(); 236 dialog.exec();
239 if (dialog.completed) { 237 if (dialog.completed) {
240 min_x = dialog.min_x; 238 min_x = dialog.min_x;
diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h
index 15d61e8ba..d76bc8154 100644
--- a/src/yuzu/configuration/configure_motion_touch.h
+++ b/src/yuzu/configuration/configure_motion_touch.h
@@ -30,7 +30,7 @@ class CalibrationConfigurationDialog : public QDialog {
30 Q_OBJECT 30 Q_OBJECT
31public: 31public:
32 explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port, 32 explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port,
33 u8 pad_index, u16 client_id); 33 u8 pad_index);
34 ~CalibrationConfigurationDialog() override; 34 ~CalibrationConfigurationDialog() override;
35 35
36private: 36private:
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 0ba7c07cc..24bfa4d34 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -60,6 +60,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
60#include <QPushButton> 60#include <QPushButton>
61#include <QShortcut> 61#include <QShortcut>
62#include <QStatusBar> 62#include <QStatusBar>
63#include <QString>
63#include <QSysInfo> 64#include <QSysInfo>
64#include <QUrl> 65#include <QUrl>
65#include <QtConcurrent/QtConcurrent> 66#include <QtConcurrent/QtConcurrent>
@@ -854,8 +855,7 @@ void GMainWindow::InitializeHotkeys() {
854 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), 855 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
855 &QShortcut::activated, this, [&] { 856 &QShortcut::activated, this, [&] {
856 Settings::values.mouse_panning = !Settings::values.mouse_panning; 857 Settings::values.mouse_panning = !Settings::values.mouse_panning;
857 if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { 858 if (Settings::values.mouse_panning) {
858 mouse_hide_timer.start();
859 render_window->installEventFilter(render_window); 859 render_window->installEventFilter(render_window);
860 render_window->setAttribute(Qt::WA_Hover, true); 860 render_window->setAttribute(Qt::WA_Hover, true);
861 } 861 }
@@ -1208,11 +1208,14 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1208 renderer_status_button->setDisabled(true); 1208 renderer_status_button->setDisabled(true);
1209 1209
1210 if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { 1210 if (UISettings::values.hide_mouse || Settings::values.mouse_panning) {
1211 mouse_hide_timer.start();
1212 render_window->installEventFilter(render_window); 1211 render_window->installEventFilter(render_window);
1213 render_window->setAttribute(Qt::WA_Hover, true); 1212 render_window->setAttribute(Qt::WA_Hover, true);
1214 } 1213 }
1215 1214
1215 if (UISettings::values.hide_mouse) {
1216 mouse_hide_timer.start();
1217 }
1218
1216 std::string title_name; 1219 std::string title_name;
1217 std::string title_version; 1220 std::string title_version;
1218 const auto res = system.GetGameName(title_name); 1221 const auto res = system.GetGameName(title_name);
@@ -2372,12 +2375,15 @@ void GMainWindow::OnConfigure() {
2372 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { 2375 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
2373 render_window->installEventFilter(render_window); 2376 render_window->installEventFilter(render_window);
2374 render_window->setAttribute(Qt::WA_Hover, true); 2377 render_window->setAttribute(Qt::WA_Hover, true);
2375 mouse_hide_timer.start();
2376 } else { 2378 } else {
2377 render_window->removeEventFilter(render_window); 2379 render_window->removeEventFilter(render_window);
2378 render_window->setAttribute(Qt::WA_Hover, false); 2380 render_window->setAttribute(Qt::WA_Hover, false);
2379 } 2381 }
2380 2382
2383 if (UISettings::values.hide_mouse) {
2384 mouse_hide_timer.start();
2385 }
2386
2381 UpdateStatusButtons(); 2387 UpdateStatusButtons();
2382} 2388}
2383 2389
@@ -2615,8 +2621,7 @@ void GMainWindow::UpdateUISettings() {
2615} 2621}
2616 2622
2617void GMainWindow::HideMouseCursor() { 2623void GMainWindow::HideMouseCursor() {
2618 if (emu_thread == nullptr || 2624 if (emu_thread == nullptr && UISettings::values.hide_mouse) {
2619 (!UISettings::values.hide_mouse && !Settings::values.mouse_panning)) {
2620 mouse_hide_timer.stop(); 2625 mouse_hide_timer.stop();
2621 ShowMouseCursor(); 2626 ShowMouseCursor();
2622 return; 2627 return;
@@ -2626,8 +2631,7 @@ void GMainWindow::HideMouseCursor() {
2626 2631
2627void GMainWindow::ShowMouseCursor() { 2632void GMainWindow::ShowMouseCursor() {
2628 render_window->unsetCursor(); 2633 render_window->unsetCursor();
2629 if (emu_thread != nullptr && 2634 if (emu_thread != nullptr && UISettings::values.hide_mouse) {
2630 (UISettings::values.hide_mouse || Settings::values.mouse_panning)) {
2631 mouse_hide_timer.start(); 2635 mouse_hide_timer.start();
2632 } 2636 }
2633} 2637}
@@ -3058,6 +3062,14 @@ int main(int argc, char* argv[]) {
3058 chdir(bin_path.c_str()); 3062 chdir(bin_path.c_str());
3059#endif 3063#endif
3060 3064
3065#ifdef __linux__
3066 // Set the DISPLAY variable in order to open web browsers
3067 // TODO (lat9nq): Find a better solution for AppImages to start external applications
3068 if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
3069 qputenv("DISPLAY", ":0");
3070 }
3071#endif
3072
3061 // Enables the core to make the qt created contexts current on std::threads 3073 // Enables the core to make the qt created contexts current on std::threads
3062 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 3074 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
3063 QApplication app(argc, argv); 3075 QApplication app(argc, argv);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 7e391ab89..ce8b7c218 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -35,18 +35,36 @@ void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
35 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0); 35 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
36} 36}
37 37
38MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
39 switch (button) {
40 case SDL_BUTTON_LEFT:
41 return MouseInput::MouseButton::Left;
42 case SDL_BUTTON_RIGHT:
43 return MouseInput::MouseButton::Right;
44 case SDL_BUTTON_MIDDLE:
45 return MouseInput::MouseButton::Wheel;
46 case SDL_BUTTON_X1:
47 return MouseInput::MouseButton::Backward;
48 case SDL_BUTTON_X2:
49 return MouseInput::MouseButton::Forward;
50 default:
51 return MouseInput::MouseButton::Undefined;
52 }
53}
54
38void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 55void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
56 const auto mouse_button = SDLButtonToMouseButton(button);
39 if (button == SDL_BUTTON_LEFT) { 57 if (button == SDL_BUTTON_LEFT) {
40 if (state == SDL_PRESSED) { 58 if (state == SDL_PRESSED) {
41 TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); 59 TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
42 } else { 60 } else {
43 TouchReleased(0); 61 TouchReleased(0);
44 } 62 }
45 } else if (button == SDL_BUTTON_RIGHT) { 63 } else {
46 if (state == SDL_PRESSED) { 64 if (state == SDL_PRESSED) {
47 input_subsystem->GetMouse()->PressButton(x, y, button); 65 input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
48 } else { 66 } else {
49 input_subsystem->GetMouse()->ReleaseButton(button); 67 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
50 } 68 }
51 } 69 }
52} 70}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 51a12a6a9..0e17bbca7 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -18,6 +18,10 @@ namespace InputCommon {
18class InputSubsystem; 18class InputSubsystem;
19} 19}
20 20
21namespace MouseInput {
22enum class MouseButton;
23}
24
21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
22public: 26public:
23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); 27 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
@@ -42,6 +46,9 @@ protected:
42 /// Called by WaitEvent when the mouse moves. 46 /// Called by WaitEvent when the mouse moves.
43 void OnMouseMotion(s32 x, s32 y); 47 void OnMouseMotion(s32 x, s32 y);
44 48
49 /// Converts a SDL mouse button into MouseInput mouse button
50 MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const;
51
45 /// Called by WaitEvent when a mouse button is pressed or released 52 /// Called by WaitEvent when a mouse button is pressed or released
46 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 53 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
47 54