diff options
54 files changed, 731 insertions, 297 deletions
diff --git a/.gitmodules b/.gitmodules index 93ba9b930..a0a89933d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,7 +27,7 @@      url = https://github.com/ReinUsesLisp/sirit  [submodule "mbedtls"]      path = externals/mbedtls -    url = https://github.com/DarkLordZach/mbedtls +    url = https://github.com/yuzu-emu/mbedtls  [submodule "libzip"]      path = externals/libzip/libzip      url = https://github.com/nih-at/libzip.git diff --git a/externals/dynarmic b/externals/dynarmic -Subproject cafa687684a3e5dbe86be7150c0f8183d2ad53c +Subproject 646fd0592091c5c1e5899fa715bd7b7fcc977a3 diff --git a/externals/mbedtls b/externals/mbedtls -Subproject a280e602f3a4ae001d3a83cbc3e6e04c99c2227 +Subproject eac2416b8fdb2cb9c867a538100bf95326bad75 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)      # /Zo                 - Enhanced debug info for optimized builds      # /permissive-        - Enables stricter C++ standards conformance checks      # /EHsc               - C++-only exception handling semantics +    # /utf-8              - Set source and execution character sets to UTF-8      # /volatile:iso       - Use strict standards-compliant volatile semantics.      # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates      # /Zc:inline          - Let codegen omit inline functions in object files @@ -38,6 +39,7 @@ if (MSVC)          /permissive-          /EHsc          /std:c++latest +        /utf-8          /volatile:iso          /Zc:externConstexpr          /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);  // Generic function to get last error message.  // Call directly after the command or use the error num.  // This function might change the error code. -// Defined in Misc.cpp. +// Defined in misc.cpp.  [[nodiscard]] std::string GetLastErrorMsg(); +// Like GetLastErrorMsg(), but passing an explicit error code. +// Defined in misc.cpp. +[[nodiscard]] std::string NativeErrorToString(int e); +  #define DECLARE_ENUM_FLAG_OPERATORS(type)                                                          \      [[nodiscard]] constexpr type operator|(type a, type b) noexcept {                              \          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() {      boost::context::detail::jump_fcontext(impl->rewind_context, this);  } -void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { -    ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); -    ASSERT_MSG(to != nullptr, "Next fiber is null!"); -    to->impl->guard.lock(); -    to->impl->previous_fiber = from; -    auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); -    ASSERT(from->impl->previous_fiber != nullptr); -    from->impl->previous_fiber->impl->context = transfer.fctx; -    from->impl->previous_fiber->impl->guard.unlock(); -    from->impl->previous_fiber.reset(); +void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) { +    to.impl->guard.lock(); +    to.impl->previous_fiber = weak_from.lock(); + +    auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to); + +    // "from" might no longer be valid if the thread was killed +    if (auto from = weak_from.lock()) { +        ASSERT(from->impl->previous_fiber != nullptr); +        from->impl->previous_fiber->impl->context = transfer.fctx; +        from->impl->previous_fiber->impl->guard.unlock(); +        from->impl->previous_fiber.reset(); +    }  }  std::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:      /// Yields control from Fiber 'from' to Fiber 'to'      /// Fiber 'from' must be the currently running fiber. -    static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to); +    static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);      [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();      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 @@  #include "common/common_funcs.h" -// Generic function to get last error message. -// Call directly after the command or use the error num. -// This function might change the error code. -std::string GetLastErrorMsg() { -    static constexpr std::size_t buff_size = 255; -    char err_str[buff_size]; - +std::string NativeErrorToString(int e) {  #ifdef _WIN32 -    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), -                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr); -    return std::string(err_str, buff_size); -#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) +    LPSTR err_str; + +    DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | +                                   FORMAT_MESSAGE_IGNORE_INSERTS, +                               nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), +                               reinterpret_cast<LPSTR>(&err_str), 1, nullptr); +    if (!res) { +        return "(FormatMessageA failed to format error)"; +    } +    std::string ret(err_str); +    LocalFree(err_str); +    return ret; +#else +    char err_str[255]; +#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))      // Thread safe (GNU-specific) -    const char* str = strerror_r(errno, err_str, buff_size); +    const char* str = strerror_r(e, err_str, sizeof(err_str));      return std::string(str);  #else      // Thread safe (XSI-compliant) -    const int success = strerror_r(errno, err_str, buff_size); -    if (success != 0) { -        return {}; +    int second_err = strerror_r(e, err_str, sizeof(err_str)); +    if (second_err != 0) { +        return "(strerror_r failed to format error)";      }      return std::string(err_str); +#endif // GLIBC etc. +#endif // _WIN32 +} + +std::string GetLastErrorMsg() { +#ifdef _WIN32 +    return NativeErrorToString(GetLastError()); +#else +    return NativeErrorToString(errno);  #endif  } 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 {              gpu_core->WaitIdle();          } -        // Shutdown emulation session          services.reset();          service_manager.reset();          cheat_engine.reset();          telemetry_session.reset(); - -        // Close all CPU/threading state          cpu_manager.Shutdown(); - -        // Release the Time Manager's resources          time_manager.Shutdown(); - -        // Shutdown kernel and core timing          core_timing.Shutdown(); -        kernel.Shutdown(); - -        // Close app loader          app_loader.reset();          gpu_core.reset();          perf_stats.reset(); - -        // Clear all applets +        kernel.Shutdown();          applet_manager.ClearAll();          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() {          auto core = kernel.GetCurrentHostThreadID();          auto& scheduler = *kernel.CurrentScheduler();          Kernel::KThread* current_thread = scheduler.GetCurrentThread(); -        Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); +        Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);          ASSERT(scheduler.ContextSwitchPending());          ASSERT(core == kernel.GetCurrentHostThreadID());          scheduler.RescheduleCurrentCore(); @@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {          auto core = kernel.GetCurrentHostThreadID();          auto& scheduler = *kernel.CurrentScheduler();          Kernel::KThread* current_thread = scheduler.GetCurrentThread(); -        Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); +        Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);          ASSERT(scheduler.ContextSwitchPending());          ASSERT(core == kernel.GetCurrentHostThreadID());          scheduler.RescheduleCurrentCore(); @@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {          scheduler.Unload(scheduler.GetCurrentThread());          auto& next_scheduler = kernel.Scheduler(current_core); -        Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); +        Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());      }      // May have changed scheduler @@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {          auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();          data.is_running = true; -        Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); +        Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());          data.is_running = false;          data.is_paused = true;          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              }          }      } - -    mbedtls_cipher_finish(context, nullptr, nullptr);  }  template <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() {      }      guard.unlock(); -    Common::Fiber::YieldTo(*old_context, switch_fiber); +    Common::Fiber::YieldTo(*old_context, *switch_fiber);      /// When a thread wakes up, the scheduler may have changed to other in another core.      auto& next_scheduler = *system.Kernel().CurrentScheduler();      next_scheduler.SwitchContextStep2(); @@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {                      break;                  }              } -            std::shared_ptr<Common::Fiber>* next_context; -            if (next_thread != nullptr) { -                next_context = &next_thread->GetHostContext(); -            } else { -                next_context = &idle_thread->GetHostContext(); -            } -            Common::Fiber::YieldTo(switch_fiber, *next_context); +            auto thread = next_thread ? next_thread : idle_thread; +            Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());          } while (!is_switch_pending());      }  } @@ -800,9 +795,9 @@ void KScheduler::Initialize() {      std::string name = "Idle Thread Id:" + std::to_string(core_id);      std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();      void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); -    auto thread_res = KThread::Create(system, ThreadType::Main, name, 0, -                                      KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0, -                                      nullptr, std::move(init_func), init_func_parameter); +    auto thread_res = KThread::CreateThread( +        system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0, +        static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);      idle_thread = thread_res.Unwrap().get();  } 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() {      return host_context;  } -ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, -                                                    std::string name, VAddr entry_point, -                                                    u32 priority, u64 arg, s32 processor_id, -                                                    VAddr stack_top, Process* owner_process) { -    std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); -    void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); -    return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, -                  owner_process, std::move(init_func), init_func_parameter); -} - -ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, -                                                    std::string name, VAddr entry_point, -                                                    u32 priority, u64 arg, s32 processor_id, -                                                    VAddr stack_top, Process* owner_process, -                                                    std::function<void(void*)>&& thread_start_func, -                                                    void* thread_start_parameter) { +ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system, +                                                          ThreadType type_flags, std::string name, +                                                          VAddr entry_point, u32 priority, u64 arg, +                                                          s32 processor_id, VAddr stack_top, +                                                          Process* owner_process) {      auto& kernel = system.Kernel();      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      auto& scheduler = kernel.GlobalSchedulerContext();      scheduler.AddThread(thread); -    thread->host_context = -        std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); -      return MakeResult<std::shared_ptr<KThread>>(std::move(thread));  } +ResultVal<std::shared_ptr<KThread>> KThread::CreateThread( +    Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, +    u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, +    std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) { +    auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg, +                                      processor_id, stack_top, owner_process); + +    if (thread_result.Succeeded()) { +        (*thread_result)->host_context = +            std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); +    } + +    return thread_result; +} + +ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread( +    Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, +    u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) { +    std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); + +    void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + +    return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id, +                        stack_top, owner_process, std::move(init_func), init_func_parameter); +} +  KThread* GetCurrentThreadPointer(KernelCore& kernel) {      return kernel.GetCurrentEmuThread();  } 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:      using WaiterList = boost::intrusive::list<KThread>;      /** -     * Creates and returns a new thread. The new thread is immediately scheduled +     * Creates and returns a new thread.       * @param system The instance of the whole system       * @param name The friendly name desired for the thread       * @param entry_point The address at which the thread should start execution @@ -127,12 +127,12 @@ public:       * @param owner_process The parent process for the thread, if null, it's a kernel thread       * @return A shared pointer to the newly created thread       */ -    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( +    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(          Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,          u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);      /** -     * Creates and returns a new thread. The new thread is immediately scheduled +     * Creates and returns a new thread, with a specified entry point.       * @param system The instance of the whole system       * @param name The friendly name desired for the thread       * @param entry_point The address at which the thread should start execution @@ -145,11 +145,27 @@ public:       * @param thread_start_parameter The parameter which will passed to host context on init       * @return A shared pointer to the newly created thread       */ -    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( +    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(          Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,          u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,          std::function<void(void*)>&& thread_start_func, void* thread_start_parameter); +    /** +     * Creates and returns a new thread for the emulated "user" process. +     * @param system The instance of the whole system +     * @param name The friendly name desired for the thread +     * @param entry_point The address at which the thread should start execution +     * @param priority The thread's priority +     * @param arg User data to pass to the thread +     * @param processor_id The ID(s) of the processors on which the thread is desired to be run +     * @param stack_top The address of the thread's stack top +     * @param owner_process The parent process for the thread, if null, it's a kernel thread +     * @return A shared pointer to the newly created thread +     */ +    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread( +        Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, +        u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); +      [[nodiscard]] std::string GetName() const override {          return name;      } 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 {              std::string name = "Suspend Thread Id:" + std::to_string(i);              std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();              void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); -            auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0, -                                              0, 0, static_cast<u32>(i), 0, nullptr, -                                              std::move(init_func), init_func_parameter); +            auto thread_res = KThread::CreateThread( +                system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0, +                nullptr, std::move(init_func), init_func_parameter);              suspend_threads[i] = std::move(thread_res).Unwrap();          } @@ -221,10 +221,9 @@ struct KernelCore::Impl {      // Gets the dummy KThread for the caller, allocating a new one if this is the first time      KThread* GetHostDummyThread() {          const thread_local auto thread = -            KThread::Create( +            KThread::CreateThread(                  system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0, -                KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr, -                []([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr) +                KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)                  .Unwrap();          return thread.get();      } 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 {  void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {      const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();      ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); -    auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, -                                      owner_process.GetIdealCoreId(), stack_top, &owner_process); +    auto thread_res = +        KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0, +                                  owner_process.GetIdealCoreId(), stack_top, &owner_process);      std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap(); 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      std::shared_ptr<KThread> thread;      {          KScopedLightLock lk{process.GetStateLock()}; -        CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority, -                                               arg, core_id, stack_bottom, &process)); +        CASCADE_RESULT(thread, +                       KThread::CreateUserThread(system, ThreadType::User, "", entry_point, +                                                 priority, arg, core_id, stack_bottom, &process));      }      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 @@  #include <limits>  #include <utility>  #include <vector> +#include "common/common_funcs.h"  #ifdef _WIN32  #define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname @@ -90,15 +91,36 @@ LINGER MakeLinger(bool enable, u32 linger_value) {      return value;  } -int LastError() { -    return WSAGetLastError(); -} -  bool EnableNonBlock(SOCKET fd, bool enable) {      u_long value = enable ? 1 : 0;      return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;  } +Errno TranslateNativeError(int e) { +    switch (e) { +    case WSAEBADF: +        return Errno::BADF; +    case WSAEINVAL: +        return Errno::INVAL; +    case WSAEMFILE: +        return Errno::MFILE; +    case WSAENOTCONN: +        return Errno::NOTCONN; +    case WSAEWOULDBLOCK: +        return Errno::AGAIN; +    case WSAECONNREFUSED: +        return Errno::CONNREFUSED; +    case WSAEHOSTUNREACH: +        return Errno::HOSTUNREACH; +    case WSAENETDOWN: +        return Errno::NETDOWN; +    case WSAENETUNREACH: +        return Errno::NETUNREACH; +    default: +        return Errno::OTHER; +    } +} +  #elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX  using SOCKET = int; @@ -108,9 +130,6 @@ using ULONG = u64;  constexpr SOCKET INVALID_SOCKET = -1;  constexpr SOCKET SOCKET_ERROR = -1; -constexpr int WSAEWOULDBLOCK = EAGAIN; -constexpr int WSAENOTCONN = ENOTCONN; -  constexpr int SD_RECEIVE = SHUT_RD;  constexpr int SD_SEND = SHUT_WR;  constexpr int SD_BOTH = SHUT_RDWR; @@ -162,10 +181,6 @@ linger MakeLinger(bool enable, u32 linger_value) {      return value;  } -int LastError() { -    return errno; -} -  bool EnableNonBlock(int fd, bool enable) {      int flags = fcntl(fd, F_GETFD);      if (flags == -1) { @@ -179,8 +194,43 @@ bool EnableNonBlock(int fd, bool enable) {      return fcntl(fd, F_SETFD, flags) == 0;  } +Errno TranslateNativeError(int e) { +    switch (e) { +    case EBADF: +        return Errno::BADF; +    case EINVAL: +        return Errno::INVAL; +    case EMFILE: +        return Errno::MFILE; +    case ENOTCONN: +        return Errno::NOTCONN; +    case EAGAIN: +        return Errno::AGAIN; +    case ECONNREFUSED: +        return Errno::CONNREFUSED; +    case EHOSTUNREACH: +        return Errno::HOSTUNREACH; +    case ENETDOWN: +        return Errno::NETDOWN; +    case ENETUNREACH: +        return Errno::NETUNREACH; +    default: +        return Errno::OTHER; +    } +} +  #endif +Errno GetAndLogLastError() { +#ifdef _WIN32 +    int e = WSAGetLastError(); +#else +    int e = errno; +#endif +    LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); +    return TranslateNativeError(e); +} +  int TranslateDomain(Domain domain) {      switch (domain) {      case Domain::INET: @@ -290,9 +340,7 @@ Errno SetSockOpt(SOCKET fd, int option, T value) {      if (result != SOCKET_ERROR) {          return Errno::SUCCESS;      } -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return Errno::SUCCESS; +    return GetAndLogLastError();  }  } // Anonymous namespace @@ -308,14 +356,12 @@ NetworkInstance::~NetworkInstance() {  std::pair<IPv4Address, Errno> GetHostIPv4Address() {      std::array<char, 256> name{};      if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { -        UNIMPLEMENTED_MSG("Unhandled gethostname error"); -        return {IPv4Address{}, Errno::SUCCESS}; +        return {IPv4Address{}, GetAndLogLastError()};      }      hostent* const ent = gethostbyname(name.data());      if (!ent) { -        UNIMPLEMENTED_MSG("Unhandled gethostbyname error"); -        return {IPv4Address{}, Errno::SUCCESS}; +        return {IPv4Address{}, GetAndLogLastError()};      }      if (ent->h_addr_list == nullptr) {          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) {      ASSERT(result == SOCKET_ERROR); -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return {-1, Errno::SUCCESS}; +    return {-1, GetAndLogLastError()};  }  Socket::~Socket() { @@ -380,9 +424,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {          return Errno::SUCCESS;      } -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return Errno::SUCCESS; +    return GetAndLogLastError();  }  std::pair<Socket::AcceptResult, Errno> Socket::Accept() { @@ -391,9 +433,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {      const SOCKET new_socket = accept(fd, &addr, &addrlen);      if (new_socket == INVALID_SOCKET) { -        const int ec = LastError(); -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return {AcceptResult{}, Errno::SUCCESS}; +        return {AcceptResult{}, GetAndLogLastError()};      }      AcceptResult result; @@ -412,23 +452,14 @@ Errno Socket::Connect(SockAddrIn addr_in) {          return Errno::SUCCESS;      } -    switch (const int ec = LastError()) { -    case WSAEWOULDBLOCK: -        LOG_DEBUG(Service, "EAGAIN generated"); -        return Errno::AGAIN; -    default: -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return Errno::SUCCESS; -    } +    return GetAndLogLastError();  }  std::pair<SockAddrIn, Errno> Socket::GetPeerName() {      sockaddr addr;      socklen_t addrlen = sizeof(addr);      if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { -        const int ec = LastError(); -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return {SockAddrIn{}, Errno::SUCCESS}; +        return {SockAddrIn{}, GetAndLogLastError()};      }      ASSERT(addrlen == sizeof(sockaddr_in)); @@ -439,9 +470,7 @@ std::pair<SockAddrIn, Errno> Socket::GetSockName() {      sockaddr addr;      socklen_t addrlen = sizeof(addr);      if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { -        const int ec = LastError(); -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return {SockAddrIn{}, Errno::SUCCESS}; +        return {SockAddrIn{}, GetAndLogLastError()};      }      ASSERT(addrlen == sizeof(sockaddr_in)); @@ -454,9 +483,7 @@ Errno Socket::Bind(SockAddrIn addr) {          return Errno::SUCCESS;      } -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return Errno::SUCCESS; +    return GetAndLogLastError();  }  Errno Socket::Listen(s32 backlog) { @@ -464,9 +491,7 @@ Errno Socket::Listen(s32 backlog) {          return Errno::SUCCESS;      } -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return Errno::SUCCESS; +    return GetAndLogLastError();  }  Errno Socket::Shutdown(ShutdownHow how) { @@ -489,14 +514,7 @@ Errno Socket::Shutdown(ShutdownHow how) {          return Errno::SUCCESS;      } -    switch (const int ec = LastError()) { -    case WSAENOTCONN: -        LOG_ERROR(Service, "ENOTCONN generated"); -        return Errno::NOTCONN; -    default: -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return Errno::SUCCESS; -    } +    return GetAndLogLastError();  }  std::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) {          return {static_cast<s32>(result), Errno::SUCCESS};      } -    switch (const int ec = LastError()) { -    case WSAEWOULDBLOCK: -        LOG_DEBUG(Service, "EAGAIN generated"); -        return {-1, Errno::AGAIN}; -    case WSAENOTCONN: -        LOG_ERROR(Service, "ENOTCONN generated"); -        return {-1, Errno::NOTCONN}; -    default: -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return {0, Errno::SUCCESS}; -    } +    return {-1, GetAndLogLastError()};  }  std::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          return {static_cast<s32>(result), Errno::SUCCESS};      } -    switch (const int ec = LastError()) { -    case WSAEWOULDBLOCK: -        LOG_DEBUG(Service, "EAGAIN generated"); -        return {-1, Errno::AGAIN}; -    case WSAENOTCONN: -        LOG_ERROR(Service, "ENOTCONN generated"); -        return {-1, Errno::NOTCONN}; -    default: -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return {-1, Errno::SUCCESS}; -    } +    return {-1, GetAndLogLastError()};  }  std::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) {          return {static_cast<s32>(result), Errno::SUCCESS};      } -    const int ec = LastError(); -    switch (ec) { -    case WSAEWOULDBLOCK: -        LOG_DEBUG(Service, "EAGAIN generated"); -        return {-1, Errno::AGAIN}; -    case WSAENOTCONN: -        LOG_ERROR(Service, "ENOTCONN generated"); -        return {-1, Errno::NOTCONN}; -    default: -        UNREACHABLE_MSG("Unhandled host socket error={}", ec); -        return {-1, Errno::SUCCESS}; -    } +    return {-1, GetAndLogLastError()};  }  std::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,          return {static_cast<s32>(result), Errno::SUCCESS};      } -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return {-1, Errno::SUCCESS}; +    return {-1, GetAndLogLastError()};  }  Errno Socket::Close() { @@ -642,9 +627,7 @@ Errno Socket::SetNonBlock(bool enable) {      if (EnableNonBlock(fd, enable)) {          return Errno::SUCCESS;      } -    const int ec = LastError(); -    UNREACHABLE_MSG("Unhandled host socket error={}", ec); -    return Errno::SUCCESS; +    return GetAndLogLastError();  }  bool 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 @@  #include <array>  #include <utility> +#include "common/common_funcs.h"  #include "common/common_types.h"  namespace Network { @@ -21,6 +22,11 @@ enum class Errno {      MFILE,      NOTCONN,      AGAIN, +    CONNREFUSED, +    HOSTUNREACH, +    NETDOWN, +    NETUNREACH, +    OTHER,  };  /// 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 {  class KeyButton final : public Input::ButtonDevice {  public: -    explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) -        : key_button_list(std::move(key_button_list_)) {} +    explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_) +        : key_button_list(std::move(key_button_list_)), toggle(toggle_) {}      ~KeyButton() override;      bool GetStatus() const override { +        if (toggle) { +            return toggled_status.load(std::memory_order_relaxed); +        }          return status.load();      } +    void ToggleButton() { +        if (lock) { +            return; +        } +        lock = true; +        const bool old_toggle_status = toggled_status.load(); +        toggled_status.store(!old_toggle_status); +    } + +    void UnlockButton() { +        lock = false; +    } +      friend class KeyButtonList;  private:      std::shared_ptr<KeyButtonList> key_button_list;      std::atomic<bool> status{false}; +    std::atomic<bool> toggled_status{false}; +    bool lock{false}; +    const bool toggle;  };  struct KeyButtonPair { @@ -51,6 +70,11 @@ public:          for (const KeyButtonPair& pair : list) {              if (pair.key_code == key_code) {                  pair.key_button->status.store(pressed); +                if (pressed) { +                    pair.key_button->ToggleButton(); +                } else { +                    pair.key_button->UnlockButton(); +                }              }          }      } @@ -75,7 +99,8 @@ KeyButton::~KeyButton() {  std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {      const int key_code = params.Get("code", 0); -    std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); +    const bool toggle = params.Get("toggle", false); +    std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle);      key_button_list->AddKeyButton(key_code, button.get());      return button;  } 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() {      });  } -void Mouse::PressButton(int x, int y, int button_) { +void Mouse::PressButton(int x, int y, MouseButton button_) {      const auto button_index = static_cast<std::size_t>(button_);      if (button_index >= mouse_info.size()) {          return; @@ -67,7 +67,7 @@ void Mouse::PressButton(int x, int y, int button_) {      const auto button = 1U << button_index;      buttons |= static_cast<u16>(button); -    last_button = static_cast<MouseButton>(button_index); +    last_button = button_;      mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);      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) {      }  } -void Mouse::ReleaseButton(int button_) { +void Mouse::ReleaseButton(MouseButton button_) {      const auto button_index = static_cast<std::size_t>(button_);      if (button_index >= mouse_info.size()) {          return; @@ -152,11 +152,52 @@ void Mouse::BeginConfiguration() {  void Mouse::EndConfiguration() {      buttons = 0; +    for (MouseInfo& info : mouse_info) { +        info.tilt_speed = 0; +        info.data.pressed = false; +        info.data.axis = {0, 0}; +    }      last_button = MouseButton::Undefined;      mouse_queue.Clear();      configuring = false;  } +bool Mouse::ToggleButton(std::size_t button_) { +    if (button_ >= mouse_info.size()) { +        return false; +    } +    const auto button = 1U << button_; +    const bool button_state = (toggle_buttons & button) != 0; +    const bool button_lock = (lock_buttons & button) != 0; + +    if (button_lock) { +        return button_state; +    } + +    lock_buttons |= static_cast<u16>(button); + +    if (button_state) { +        toggle_buttons &= static_cast<u16>(0xFF - button); +    } else { +        toggle_buttons |= static_cast<u16>(button); +    } + +    return !button_state; +} + +bool Mouse::UnlockButton(std::size_t button_) { +    if (button_ >= mouse_info.size()) { +        return false; +    } + +    const auto button = 1U << button_; +    const bool button_state = (toggle_buttons & button) != 0; + +    lock_buttons &= static_cast<u16>(0xFF - button); + +    return button_state; +} +  Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {      return mouse_queue;  } 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 {  enum class MouseButton {      Left, -    Wheel,      Right, -    Forward, +    Wheel,      Backward, +    Forward, +    Task, +    Extra,      Undefined,  }; @@ -51,7 +53,7 @@ public:       * @param y the y-coordinate of the cursor       * @param button_ the button pressed       */ -    void PressButton(int x, int y, int button_); +    void PressButton(int x, int y, MouseButton button_);      /**       * Signals that mouse has moved. @@ -65,7 +67,10 @@ public:      /**       * Signals that a motion sensor tilt has ended.       */ -    void ReleaseButton(int button_); +    void ReleaseButton(MouseButton button_); + +    [[nodiscard]] bool ToggleButton(std::size_t button_); +    [[nodiscard]] bool UnlockButton(std::size_t button_);      [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();      [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const; @@ -92,9 +97,11 @@ private:      };      u16 buttons{}; +    u16 toggle_buttons{}; +    u16 lock_buttons{};      std::thread update_thread;      MouseButton last_button{MouseButton::Undefined}; -    std::array<MouseInfo, 5> mouse_info; +    std::array<MouseInfo, 7> mouse_info;      Common::SPSCQueue<MouseStatus> mouse_queue;      bool configuring{false};      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 {  class MouseButton final : public Input::ButtonDevice {  public: -    explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_) -        : button(button_), mouse_input(mouse_input_) {} +    explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_) +        : button(button_), toggle(toggle_), mouse_input(mouse_input_) {}      bool GetStatus() const override { -        return mouse_input->GetMouseState(button).pressed; +        const bool button_state = mouse_input->GetMouseState(button).pressed; +        if (!toggle) { +            return button_state; +        } + +        if (button_state) { +            return mouse_input->ToggleButton(button); +        } +        return mouse_input->UnlockButton(button);      }  private:      const u32 button; -    const MouseInput::Mouse* mouse_input; +    const bool toggle; +    MouseInput::Mouse* mouse_input;  };  MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) @@ -32,8 +41,9 @@ MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_  std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(      const Common::ParamPackage& params) {      const auto button_id = params.Get("button", 0); +    const auto toggle = params.Get("toggle", false); -    return std::make_unique<MouseButton>(button_id, mouse_input.get()); +    return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get());  }  Common::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 @@  #include <chrono>  #include <cstring>  #include <functional> +#include <random>  #include <thread>  #include <boost/asio.hpp>  #include "common/logging/log.h" @@ -26,10 +27,10 @@ class Socket {  public:      using clock = std::chrono::system_clock; -    explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_, +    explicit Socket(const std::string& host, u16 port, std::size_t pad_index_,                      SocketCallback callback_)          : callback(std::move(callback_)), timer(io_service), -          socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_), +          socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()),            pad_index(pad_index_) {          boost::system::error_code ec{};          auto ipv4 = boost::asio::ip::make_address_v4(host, ec); @@ -63,6 +64,11 @@ public:      }  private: +    u32 GenerateRandomClientId() const { +        std::random_device device; +        return device(); +    } +      void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {          if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {              switch (*type) { @@ -115,7 +121,7 @@ private:      boost::asio::basic_waitable_timer<clock> timer;      udp::socket socket; -    u32 client_id{}; +    const u32 client_id;      std::size_t pad_index{};      static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); @@ -203,7 +209,7 @@ void Client::ReloadSockets() {                  LOG_ERROR(Input, "Duplicated UDP servers found");                  continue;              } -            StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872); +            StartCommunication(client++, udp_input_address, udp_input_port, pad);          }      }  } @@ -277,7 +283,7 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {  }  void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, -                                std::size_t pad_index, u32 client_id) { +                                std::size_t pad_index) {      SocketCallback callback{[this](Response::Version version) { OnVersion(version); },                              [this](Response::PortInfo info) { OnPortInfo(info); },                              [this, client](Response::PadData data) { OnPadData(data, client); }}; @@ -287,7 +293,7 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16      clients[client].port = port;      clients[client].pad_index = pad_index;      clients[client].active = 0; -    clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); +    clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback);      clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};      // Set motion parameters      // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode @@ -416,7 +422,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {      return pad_queue;  } -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, +void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,                         const std::function<void()>& success_callback,                         const std::function<void()>& failure_callback) {      std::thread([=] { @@ -426,7 +432,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,              .port_info = [](Response::PortInfo) {},              .pad_data = [&](Response::PadData) { success_event.Set(); },          }; -        Socket socket{host, port, pad_index, client_id, std::move(callback)}; +        Socket socket{host, port, pad_index, std::move(callback)};          std::thread worker_thread{SocketLoop, &socket};          const bool result = success_event.WaitFor(std::chrono::seconds(5));          socket.Stop(); @@ -440,7 +446,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,  }  CalibrationConfigurationJob::CalibrationConfigurationJob( -    const std::string& host, u16 port, std::size_t pad_index, u32 client_id, +    const std::string& host, u16 port, std::size_t pad_index,      std::function<void(Status)> status_callback,      std::function<void(u16, u16, u16, u16)> data_callback) { @@ -485,7 +491,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(                                          complete_event.Set();                                      }                                  }}; -        Socket socket{host, port, pad_index, client_id, std::move(callback)}; +        Socket socket{host, port, pad_index, std::move(callback)};          std::thread worker_thread{SocketLoop, &socket};          complete_event.Wait();          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:      void OnPortInfo(Response::PortInfo);      void OnPadData(Response::PadData, std::size_t client);      void StartCommunication(std::size_t client, const std::string& host, u16 port, -                            std::size_t pad_index, u32 client_id); +                            std::size_t pad_index);      void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,                              const Common::Vec3<float>& gyro); @@ -165,7 +165,7 @@ public:       * @param data_callback Called when calibration data is ready       */      explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index, -                                         u32 client_id, std::function<void(Status)> status_callback, +                                         std::function<void(Status)> status_callback,                                           std::function<void(u16, u16, u16, u16)> data_callback);      ~CalibrationConfigurationJob();      void Stop(); @@ -174,7 +174,7 @@ private:      Common::Event complete_event;  }; -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, +void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,                         const std::function<void()>& success_callback,                         const std::function<void()>& failure_callback); 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      common/param_package.cpp      common/ring_buffer.cpp      core/core_timing.cpp +    core/network/network.cpp      tests.cpp      video_core/buffer_base.cpp  ) 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() {          value++;      }      results[id] = value; -    Fiber::YieldTo(work_fibers[id], thread_fibers[id]); +    Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);  }  void TestControl1::ExecuteThread(u32 id) { @@ -76,7 +76,7 @@ void TestControl1::ExecuteThread(u32 id) {      thread_fibers[id] = thread_fiber;      work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);      items[id] = rand() % 256; -    Fiber::YieldTo(thread_fibers[id], work_fibers[id]); +    Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);      thread_fibers[id]->Exit();  } @@ -117,11 +117,11 @@ public:          for (u32 i = 0; i < 12000; i++) {              value1 += i;          } -        Fiber::YieldTo(fiber1, fiber3); +        Fiber::YieldTo(fiber1, *fiber3);          const u32 id = thread_ids.Get();          assert1 = id == 1;          value2 += 5000; -        Fiber::YieldTo(fiber1, thread_fibers[id]); +        Fiber::YieldTo(fiber1, *thread_fibers[id]);      }      void DoWork2() { @@ -129,7 +129,7 @@ public:              ;          value2 = 2000;          trap = false; -        Fiber::YieldTo(fiber2, fiber1); +        Fiber::YieldTo(fiber2, *fiber1);          assert3 = false;      } @@ -137,19 +137,19 @@ public:          const u32 id = thread_ids.Get();          assert2 = id == 0;          value1 += 1000; -        Fiber::YieldTo(fiber3, thread_fibers[id]); +        Fiber::YieldTo(fiber3, *thread_fibers[id]);      }      void ExecuteThread(u32 id);      void CallFiber1() {          const u32 id = thread_ids.Get(); -        Fiber::YieldTo(thread_fibers[id], fiber1); +        Fiber::YieldTo(thread_fibers[id], *fiber1);      }      void CallFiber2() {          const u32 id = thread_ids.Get(); -        Fiber::YieldTo(thread_fibers[id], fiber2); +        Fiber::YieldTo(thread_fibers[id], *fiber2);      }      void Exit(); @@ -241,23 +241,23 @@ public:      void DoWork1() {          value1 += 1; -        Fiber::YieldTo(fiber1, fiber2); +        Fiber::YieldTo(fiber1, *fiber2);          const u32 id = thread_ids.Get();          value3 += 1; -        Fiber::YieldTo(fiber1, thread_fibers[id]); +        Fiber::YieldTo(fiber1, *thread_fibers[id]);      }      void DoWork2() {          value2 += 1;          const u32 id = thread_ids.Get(); -        Fiber::YieldTo(fiber2, thread_fibers[id]); +        Fiber::YieldTo(fiber2, *thread_fibers[id]);      }      void ExecuteThread(u32 id);      void CallFiber1() {          const u32 id = thread_ids.Get(); -        Fiber::YieldTo(thread_fibers[id], fiber1); +        Fiber::YieldTo(thread_fibers[id], *fiber1);      }      void Exit(); @@ -332,7 +332,7 @@ public:      void Execute() {          thread_fiber = Fiber::ThreadToFiber(); -        Fiber::YieldTo(thread_fiber, fiber1); +        Fiber::YieldTo(thread_fiber, *fiber1);          thread_fiber->Exit();      } @@ -340,7 +340,7 @@ public:          fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);          if (rewinded) {              goal_reached = true; -            Fiber::YieldTo(fiber1, thread_fiber); +            Fiber::YieldTo(fiber1, *thread_fiber);          }          rewinded = true;          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 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <catch2/catch.hpp> + +#include "core/network/network.h" +#include "core/network/sockets.h" + +TEST_CASE("Network::Errors", "[core]") { +    Network::NetworkInstance network_instance; // initialize network + +    Network::Socket socks[2]; +    for (Network::Socket& sock : socks) { +        REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM, +                                Network::Protocol::TCP) == Network::Errno::SUCCESS); +    } + +    Network::SockAddrIn addr{ +        Network::Domain::INET, +        {127, 0, 0, 1}, +        1, // hopefully nobody running this test has something listening on port 1 +    }; +    REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED); + +    std::vector<u8> message{1, 2, 3, 4}; +    REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN); +} 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 @@  #include <deque>  #include <memory>  #include <mutex> +#include <numeric>  #include <span>  #include <unordered_map>  #include <vector> @@ -91,7 +92,7 @@ class BufferCache {      };  public: -    static constexpr u32 SKIP_CACHE_SIZE = 4096; +    static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;      explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,                           Tegra::Engines::Maxwell3D& maxwell3d_, @@ -240,9 +241,9 @@ private:      template <bool insert>      void ChangeRegister(BufferId buffer_id); -    void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); +    bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); -    void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); +    bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);      void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,                        std::span<BufferCopy> copies); @@ -297,6 +298,11 @@ private:      std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{}; +    std::array<u32, 16> uniform_cache_hits{}; +    std::array<u32, 16> uniform_cache_shots{}; + +    u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE; +      bool has_deleted_buffers = false;      std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty> @@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,  template <class P>  void BufferCache<P>::TickFrame() { +    // Calculate hits and shots and move hit bits to the right +    const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end()); +    const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end()); +    std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1, +                uniform_cache_hits.begin() + 1); +    std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1, +                uniform_cache_shots.begin() + 1); +    uniform_cache_hits[0] = 0; +    uniform_cache_shots[0] = 0; + +    const bool skip_preferred = hits * 256 < shots * 251; +    uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; +      delayed_destruction_ring.Tick();  } @@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32      const VAddr cpu_addr = binding.cpu_addr;      const u32 size = binding.size;      Buffer& buffer = slot_buffers[binding.buffer_id]; -    if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) { +    if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) {          if constexpr (IS_OPENGL) {              if (runtime.HasFastBufferSubData()) {                  // Fast path for Nvidia @@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32          return;      }      // Classic cached path -    SynchronizeBuffer(buffer, cpu_addr, size); +    const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size); +    if (sync_cached) { +        ++uniform_cache_hits[0]; +    } +    ++uniform_cache_shots[0]; +      if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) {          // Skip binding if it's not needed and if the bound buffer is not the fast version          // 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) {  }  template <class P> -void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { +bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {      if (buffer.CpuAddr() == 0) { -        return; +        return true;      } -    SynchronizeBufferImpl(buffer, cpu_addr, size); +    return SynchronizeBufferImpl(buffer, cpu_addr, size);  }  template <class P> -void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { +bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {      boost::container::small_vector<BufferCopy, 4> copies;      u64 total_size_bytes = 0;      u64 largest_copy = 0; @@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s          largest_copy = std::max(largest_copy, range_size);      });      if (total_size_bytes == 0) { -        return; +        return true;      }      const std::span<BufferCopy> copies_span(copies.data(), copies.size());      UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); +    return false;  }  template <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{      PixelFormat::A2B10G10R10_UINT,  }; +constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{ +    PixelFormat::R16G16_FLOAT,      PixelFormat::B10G11R11_FLOAT,  PixelFormat::R32_FLOAT, +    PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT,      PixelFormat::R32_UINT, +    PixelFormat::R16G16_SINT,       PixelFormat::R32_SINT,         PixelFormat::A8B8G8R8_UNORM, +    PixelFormat::R16G16_UNORM,      PixelFormat::A8B8G8R8_SNORM,   PixelFormat::R16G16_SNORM, +    PixelFormat::A8B8G8R8_SRGB,     PixelFormat::E5B9G9R9_FLOAT,   PixelFormat::A8B8G8R8_UINT, +    PixelFormat::A8B8G8R8_SINT,     PixelFormat::A2B10G10R10_UINT, +}; +  // TODO: How should we handle 24 bits?  constexpr std::array VIEW_CLASS_16_BITS{ @@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {      EnableRange(view, VIEW_CLASS_128_BITS);      EnableRange(view, VIEW_CLASS_96_BITS);      EnableRange(view, VIEW_CLASS_64_BITS); -    EnableRange(view, VIEW_CLASS_32_BITS);      EnableRange(view, VIEW_CLASS_16_BITS);      EnableRange(view, VIEW_CLASS_8_BITS);      EnableRange(view, VIEW_CLASS_RGTC1_RED); @@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {      EnableRange(copy, COPY_CLASS_64_BITS);      return copy;  } + +constexpr Table MakeNativeBgrViewTable() { +    Table copy = MakeViewTable(); +    EnableRange(copy, VIEW_CLASS_32_BITS); +    return copy; +} + +constexpr Table MakeNonNativeBgrViewTable() { +    Table copy = MakeViewTable(); +    EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR); +    return copy; +} + +constexpr Table MakeNativeBgrCopyTable() { +    Table copy = MakeCopyTable(); +    EnableRange(copy, VIEW_CLASS_32_BITS); +    return copy; +} + +constexpr Table MakeNonNativeBgrCopyTable() { +    Table copy = MakeCopyTable(); +    EnableRange(copy, VIEW_CLASS_32_BITS); +    return copy; +}  } // Anonymous namespace -bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) { +bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views, +                      bool native_bgr) {      if (broken_views) {          // If format views are broken, only accept formats that are identical.          return format_a == format_b;      } -    static constexpr Table TABLE = MakeViewTable(); -    return IsSupported(TABLE, format_a, format_b); +    static constexpr Table BGR_TABLE = MakeNativeBgrViewTable(); +    static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable(); +    return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);  } -bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) { -    static constexpr Table TABLE = MakeCopyTable(); -    return IsSupported(TABLE, format_a, format_b); +bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) { +    static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable(); +    static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable(); +    return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);  }  } // 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 @@  namespace VideoCore::Surface { -bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views); +bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views, +                      bool native_bgr); -bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); +bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);  } // 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      convert_float_to_depth.frag      full_screen_triangle.vert      opengl_copy_bc4.comp +    opengl_copy_bgra.comp      opengl_present.frag      opengl_present.vert      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 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 430 core + +layout (local_size_x = 4, local_size_y = 4) in; + +layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input; +layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output; + +void main() { +    vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID)); +    imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra); +} 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_)      for (auto& stage_uniforms : fast_uniforms) {          for (OGLBuffer& buffer : stage_uniforms) {              buffer.Create(); -            glNamedBufferData(buffer.handle, BufferCache::SKIP_CACHE_SIZE, nullptr, GL_STREAM_DRAW); +            glNamedBufferData(buffer.handle, BufferCache::DEFAULT_SKIP_CACHE_SIZE, nullptr, +                              GL_STREAM_DRAW);          }      }      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 = {{      {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT},                          // BC6H_UFLOAT      {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT},                            // BC6H_SFLOAT      {GL_COMPRESSED_RGBA_ASTC_4x4_KHR},                                // ASTC_2D_4X4_UNORM -    {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE},                            // B8G8R8A8_UNORM +    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},                            // B8G8R8A8_UNORM      {GL_RGBA32F, GL_RGBA, GL_FLOAT},                                  // R32G32B32A32_FLOAT      {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT},                            // R32G32B32A32_SINT      {GL_RG32F, GL_RG, GL_FLOAT},                                      // R32G32_FLOAT @@ -125,7 +125,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{      {GL_COMPRESSED_RGBA_ASTC_8x8_KHR},                                // ASTC_2D_8X8_UNORM      {GL_COMPRESSED_RGBA_ASTC_8x5_KHR},                                // ASTC_2D_8X5_UNORM      {GL_COMPRESSED_RGBA_ASTC_5x4_KHR},                                // ASTC_2D_5X4_UNORM -    {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE},                     // B8G8R8A8_UNORM +    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE},                     // B8G8R8A8_SRGB      {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT},                         // BC1_RGBA_SRGB      {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT},                         // BC2_SRGB      {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT},                         // BC3_SRGB @@ -396,6 +396,17 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {      }  } +[[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) { +    switch (format) { +    case PixelFormat::B5G6R5_UNORM: +    case PixelFormat::B8G8R8A8_UNORM: +    case PixelFormat::B8G8R8A8_SRGB: +        return true; +    default: +        return false; +    } +} +  } // Anonymous namespace  ImageBufferMap::~ImageBufferMap() { @@ -512,6 +523,9 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) {      if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {          return false;      } +    if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { +        return false; +    }      return true;  } @@ -520,6 +534,8 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,      if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {          ASSERT(src.info.type == ImageType::e3D);          util_shaders.CopyBC4(dst, src, copies); +    } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { +        util_shaders.CopyBGR(dst, src, copies);      } else {          UNREACHABLE();      } 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:      FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; +    bool HasNativeBgr() const noexcept { +        // OpenGL does not have native support for the BGR internal format +        return false; +    } +      bool HasBrokenTextureViewFormats() const noexcept {          return has_broken_texture_view_formats;      } 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 @@  #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"  #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"  #include "video_core/host_shaders/opengl_copy_bc4_comp.h" +#include "video_core/host_shaders/opengl_copy_bgra_comp.h"  #include "video_core/host_shaders/pitch_unswizzle_comp.h"  #include "video_core/renderer_opengl/gl_resource_manager.h"  #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -48,6 +49,11 @@ OGLProgram MakeProgram(std::string_view source) {      return program;  } +size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) { +    return static_cast<size_t>(copy.extent.width * copy.extent.height * +                               copy.src_subresource.num_layers); +} +  } // Anonymous namespace  UtilShaders::UtilShaders(ProgramManager& program_manager_) @@ -55,6 +61,7 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)        block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),        block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),        pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), +      copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),        copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {      const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();      swizzle_table_buffer.Create(); @@ -205,6 +212,43 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im      program_manager.RestoreGuestCompute();  } +void UtilShaders::CopyBGR(Image& dst_image, Image& src_image, +                          std::span<const VideoCommon::ImageCopy> copies) { +    static constexpr GLuint BINDING_INPUT_IMAGE = 0; +    static constexpr GLuint BINDING_OUTPUT_IMAGE = 1; +    static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0}; +    const u32 bytes_per_block = BytesPerBlock(dst_image.info.format); +    switch (bytes_per_block) { +    case 2: +        // BGR565 copy +        for (const ImageCopy& copy : copies) { +            ASSERT(copy.src_offset == zero_offset); +            ASSERT(copy.dst_offset == zero_offset); +            bgr_copy_pass.Execute(dst_image, src_image, copy); +        } +        break; +    case 4: { +        // BGRA8 copy +        program_manager.BindHostCompute(copy_bgra_program.handle); +        constexpr GLenum FORMAT = GL_RGBA8; +        for (const ImageCopy& copy : copies) { +            ASSERT(copy.src_offset == zero_offset); +            ASSERT(copy.dst_offset == zero_offset); +            glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(), +                               copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT); +            glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(), +                               copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT); +            glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth); +        } +        program_manager.RestoreGuestCompute(); +        break; +    } +    default: +        UNREACHABLE(); +        break; +    } +} +  GLenum StoreFormat(u32 bytes_per_block) {      switch (bytes_per_block) {      case 1: @@ -222,4 +266,36 @@ GLenum StoreFormat(u32 bytes_per_block) {      return GL_R8UI;  } +void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image, +                             const ImageCopy& copy) { +    if (CopyBufferCreationNeeded(copy)) { +        CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565); +    } +    // Copy from source to PBO +    glPixelStorei(GL_PACK_ALIGNMENT, 1); +    glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width); +    glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle); +    glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, +                         copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, +                         static_cast<GLsizei>(bgr16_pbo_size), nullptr); + +    // Copy from PBO to destination in reverse order +    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +    glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width); +    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle); +    glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, +                        copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, +                        nullptr); +} + +bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) { +    return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16); +} + +void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) { +    bgr16_pbo.Create(); +    bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16); +    glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY); +} +  } // 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;  struct ImageBufferMap; +class Bgr565CopyPass { +public: +    Bgr565CopyPass() = default; +    ~Bgr565CopyPass() = default; + +    void Execute(const Image& dst_image, const Image& src_image, +                 const VideoCommon::ImageCopy& copy); + +private: +    [[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy); +    void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format); + +    OGLBuffer bgr16_pbo; +    size_t bgr16_pbo_size{}; +}; +  class UtilShaders {  public:      explicit UtilShaders(ProgramManager& program_manager); @@ -36,6 +52,9 @@ public:      void CopyBC4(Image& dst_image, Image& src_image,                   std::span<const VideoCommon::ImageCopy> copies); +    void CopyBGR(Image& dst_image, Image& src_image, +                 std::span<const VideoCommon::ImageCopy> copies); +  private:      ProgramManager& program_manager; @@ -44,7 +63,10 @@ private:      OGLProgram block_linear_unswizzle_2d_program;      OGLProgram block_linear_unswizzle_3d_program;      OGLProgram pitch_unswizzle_program; +    OGLProgram copy_bgra_program;      OGLProgram copy_bc4_program; + +    Bgr565CopyPass bgr_copy_pass;  };  GLenum 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 @@  namespace Vulkan { -constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000; +constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;  struct CommandPool::Pool {      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 {          // No known Vulkan driver has broken image views          return false;      } + +    bool HasNativeBgr() const noexcept { +        // All known Vulkan drivers can natively handle BGR textures +        return true; +    }  };  class 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      if (lhs.info.type == ImageType::Linear) {          base = SubresourceBase{.level = 0, .layer = 0};      } else { -        // We are passing relaxed formats as an option, having broken views or not won't matter +        // We are passing relaxed formats as an option, having broken views/bgr or not won't matter          static constexpr bool broken_views = false; -        base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views); +        static constexpr bool native_bgr = true; +        base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);      }      if (!base) {          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            .height = std::max(image_info.size.height >> range.base.level, 1u),            .depth = std::max(image_info.size.depth >> range.base.level, 1u),        } { -    ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false), +    ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),                 "Image view format {} is incompatible with image format {}", info.format,                 image_info.format);      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,          return ImageId{};      }      const bool broken_views = runtime.HasBrokenTextureViewFormats(); +    const bool native_bgr = runtime.HasNativeBgr();      ImageId image_id;      const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {          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,              if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&                  existing.pitch == info.pitch &&                  IsPitchLinearSameSize(existing, info, strict_size) && -                IsViewCompatible(existing.format, info.format, broken_views)) { +                IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {                  image_id = existing_image_id;                  return true;              } -        } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) { +        } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views, +                                 native_bgr)) {              image_id = existing_image_id;              return true;          } @@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA      ImageInfo new_info = info;      const size_t size_bytes = CalculateGuestSizeInBytes(new_info);      const bool broken_views = runtime.HasBrokenTextureViewFormats(); +    const bool native_bgr = runtime.HasNativeBgr();      std::vector<ImageId> overlap_ids;      std::vector<ImageId> left_aliased_ids;      std::vector<ImageId> right_aliased_ids; @@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA              return;          }          static constexpr bool strict_size = true; -        const std::optional<OverlapResult> solution = -            ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views); +        const std::optional<OverlapResult> solution = ResolveOverlap( +            new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);          if (solution) {              gpu_addr = solution->gpu_addr;              cpu_addr = solution->cpu_addr; @@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA          }          static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;          const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); -        if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) { +        if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {              left_aliased_ids.push_back(overlap_id);          } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, -                                 broken_views)) { +                                 broken_views, native_bgr)) {              right_aliased_ids.push_back(overlap_id);          }      }); 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  std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,                                              VAddr cpu_addr, const ImageBase& overlap, -                                            bool strict_size, bool broken_views) { +                                            bool strict_size, bool broken_views, bool native_bgr) {      ASSERT(new_info.type != ImageType::Linear);      ASSERT(overlap.info.type != ImageType::Linear);      if (!IsLayerStrideCompatible(new_info, overlap.info)) {          return std::nullopt;      } -    if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) { +    if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) {          return std::nullopt;      }      if (gpu_addr == overlap.gpu_addr) { @@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {  std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,                                                 GPUVAddr candidate_addr, RelaxedOptions options, -                                               bool broken_views) { +                                               bool broken_views, bool native_bgr) {      const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);      if (!base) {          return std::nullopt;      }      const ImageInfo& existing = image.info;      if (False(options & RelaxedOptions::Format)) { -        if (!IsViewCompatible(existing.format, candidate.format, broken_views)) { +        if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {              return std::nullopt;          }      } @@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const  }  bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, -                   RelaxedOptions options, bool broken_views) { -    return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value(); +                   RelaxedOptions options, bool broken_views, bool native_bgr) { +    return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr) +        .has_value();  }  void 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  [[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,                                                            GPUVAddr gpu_addr, VAddr cpu_addr,                                                            const ImageBase& overlap, -                                                          bool strict_size, bool broken_views); +                                                          bool strict_size, bool broken_views, +                                                          bool native_bgr);  [[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); @@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima                                                               const ImageBase& image,                                                               GPUVAddr candidate_addr,                                                               RelaxedOptions options, -                                                             bool broken_views); +                                                             bool broken_views, bool native_bgr);  [[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, -                                 GPUVAddr candidate_addr, RelaxedOptions options, -                                 bool broken_views); +                                 GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views, +                                 bool native_bgr);  void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,                        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) {  }  void GRenderWindow::keyPressEvent(QKeyEvent* event) { -    input_subsystem->GetKeyboard()->PressKey(event->key()); +    if (!event->isAutoRepeat()) { +        input_subsystem->GetKeyboard()->PressKey(event->key()); +    }  }  void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { -    input_subsystem->GetKeyboard()->ReleaseKey(event->key()); +    if (!event->isAutoRepeat()) { +        input_subsystem->GetKeyboard()->ReleaseKey(event->key()); +    } +} + +MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) { +    switch (button) { +    case Qt::LeftButton: +        return MouseInput::MouseButton::Left; +    case Qt::RightButton: +        return MouseInput::MouseButton::Right; +    case Qt::MiddleButton: +        return MouseInput::MouseButton::Wheel; +    case Qt::BackButton: +        return MouseInput::MouseButton::Backward; +    case Qt::ForwardButton: +        return MouseInput::MouseButton::Forward; +    case Qt::TaskButton: +        return MouseInput::MouseButton::Task; +    default: +        return MouseInput::MouseButton::Extra; +    }  }  void GRenderWindow::mousePressEvent(QMouseEvent* event) { @@ -391,7 +414,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {      auto pos = event->pos();      const auto [x, y] = ScaleTouch(pos); -    input_subsystem->GetMouse()->PressButton(x, y, event->button()); +    const auto button = QtButtonToMouseButton(event->button()); +    input_subsystem->GetMouse()->PressButton(x, y, button);      if (event->button() == Qt::LeftButton) {          this->TouchPressed(x, y, 0); @@ -425,7 +449,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {          return;      } -    input_subsystem->GetMouse()->ReleaseButton(event->button()); +    const auto button = QtButtonToMouseButton(event->button()); +    input_subsystem->GetMouse()->ReleaseButton(button);      if (event->button() == Qt::LeftButton) {          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 {  class InputSubsystem;  } +namespace MouseInput { +enum class MouseButton; +} +  namespace VideoCore {  enum class LoadCallbackStage;  } @@ -149,6 +153,9 @@ public:      void keyPressEvent(QKeyEvent* event) override;      void keyReleaseEvent(QKeyEvent* event) override; +    /// Converts a Qt mouse button into MouseInput mouse button +    static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button); +      void mousePressEvent(QMouseEvent* event) override;      void mouseMoveEvent(QMouseEvent* event) override;      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{{      {QStringLiteral("Restart Emulation"),        QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},      {QStringLiteral("Stop Emulation"),           QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},      {QStringLiteral("Toggle Filter Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, -    {QStringLiteral("Toggle Mouse Panning"),     QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}}, +    {QStringLiteral("Toggle Mouse Panning"),     QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},      {QStringLiteral("Toggle Speed Limit"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},      {QStringLiteral("Toggle Status Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},  }}; @@ -508,7 +508,7 @@ void Config::ReadControlValues() {      Settings::values.emulate_analog_keyboard =          ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); -    Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool(); +    Settings::values.mouse_panning = false;      Settings::values.mouse_panning_sensitivity =          ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); @@ -1182,7 +1182,6 @@ void Config::SaveControlValues() {      WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);      WriteSetting(QStringLiteral("emulate_analog_keyboard"),                   Settings::values.emulate_analog_keyboard, false); -    WriteSetting(QStringLiteral("mouse_panning"), Settings::values.mouse_panning, false);      WriteSetting(QStringLiteral("mouse_panning_sensitivity"),                   Settings::values.mouse_panning_sensitivity, 1.0f);      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 @@  #include "input_common/mouse/mouse_poller.h"  #include "input_common/udp/udp.h"  #include "ui_configure_input_player.h" +#include "yuzu/bootmanager.h"  #include "yuzu/configuration/config.h"  #include "yuzu/configuration/configure_input_player.h"  #include "yuzu/configuration/configure_input_player_widget.h" @@ -104,7 +105,9 @@ QString ButtonToText(const Common::ParamPackage& param) {      }      if (param.Get("engine", "") == "keyboard") { -        return GetKeyName(param.Get("code", 0)); +        const QString button_str = GetKeyName(param.Get("code", 0)); +        const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); +        return QObject::tr("%1%2").arg(toggle, button_str);      }      if (param.Get("engine", "") == "gcpad") { @@ -156,7 +159,8 @@ QString ButtonToText(const Common::ParamPackage& param) {      if (param.Get("engine", "") == "mouse") {          if (param.Has("button")) {              const QString button_str = QString::number(int(param.Get("button", 0))); -            return QObject::tr("Click %1").arg(button_str); +            const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); +            return QObject::tr("%1Click %2").arg(toggle, button_str);          }          return GetKeyName(param.Get("code", 0));      } @@ -300,6 +304,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i                          buttons_param[button_id].Clear();                          button_map[button_id]->setText(tr("[not set]"));                      }); +                    context_menu.addAction(tr("Toggle button"), [&] { +                        const bool toggle_value = !buttons_param[button_id].Get("toggle", false); +                        buttons_param[button_id].Set("toggle", toggle_value); +                        button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); +                    });                      context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));                      ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);                  }); @@ -412,6 +421,15 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i                          analogs_param[analog_id].Set("modifier", "");                          analog_map_modifier_button[analog_id]->setText(tr("[not set]"));                      }); +                    context_menu.addAction(tr("Toggle button"), [&] { +                        Common::ParamPackage modifier_param = +                            Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")}; +                        const bool toggle_value = !modifier_param.Get("toggle", false); +                        modifier_param.Set("toggle", toggle_value); +                        analogs_param[analog_id].Set("modifier", modifier_param.Serialize()); +                        analog_map_modifier_button[analog_id]->setText( +                            ButtonToText(modifier_param)); +                    });                      context_menu.exec(                          analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));                  }); @@ -1345,7 +1363,8 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {          return;      } -    input_subsystem->GetMouse()->PressButton(0, 0, event->button()); +    const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); +    input_subsystem->GetMouse()->PressButton(0, 0, button);  }  void 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 @@  CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,                                                                 const std::string& host, u16 port, -                                                               u8 pad_index, u16 client_id) +                                                               u8 pad_index)      : QDialog(parent) {      layout = new QVBoxLayout;      status_label = new QLabel(tr("Communicating with the server...")); @@ -41,7 +41,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,      using namespace InputCommon::CemuhookUDP;      job = std::make_unique<CalibrationConfigurationJob>( -        host, port, pad_index, client_id, +        host, port, pad_index,          [this](CalibrationConfigurationJob::Status status) {              QString text;              switch (status) { @@ -218,7 +218,6 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() {      udp_test_in_progress = true;      InputCommon::CemuhookUDP::TestCommunication(          ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), 0, -        24872,          [this] {              LOG_INFO(Frontend, "UDP input test success");              QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); @@ -233,8 +232,7 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() {      ui->touch_calibration_config->setEnabled(false);      ui->touch_calibration_config->setText(tr("Configuring"));      CalibrationConfigurationDialog dialog(this, ui->udp_server->text().toStdString(), -                                          static_cast<u16>(ui->udp_port->text().toUInt()), 0, -                                          24872); +                                          static_cast<u16>(ui->udp_port->text().toUInt()), 0);      dialog.exec();      if (dialog.completed) {          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 {      Q_OBJECT  public:      explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port, -                                            u8 pad_index, u16 client_id); +                                            u8 pad_index);      ~CalibrationConfigurationDialog() override;  private: 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  #include <QPushButton>  #include <QShortcut>  #include <QStatusBar> +#include <QString>  #include <QSysInfo>  #include <QUrl>  #include <QtConcurrent/QtConcurrent> @@ -854,8 +855,7 @@ void GMainWindow::InitializeHotkeys() {      connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),              &QShortcut::activated, this, [&] {                  Settings::values.mouse_panning = !Settings::values.mouse_panning; -                if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { -                    mouse_hide_timer.start(); +                if (Settings::values.mouse_panning) {                      render_window->installEventFilter(render_window);                      render_window->setAttribute(Qt::WA_Hover, true);                  } @@ -1208,11 +1208,14 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {      renderer_status_button->setDisabled(true);      if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { -        mouse_hide_timer.start();          render_window->installEventFilter(render_window);          render_window->setAttribute(Qt::WA_Hover, true);      } +    if (UISettings::values.hide_mouse) { +        mouse_hide_timer.start(); +    } +      std::string title_name;      std::string title_version;      const auto res = system.GetGameName(title_name); @@ -2372,12 +2375,15 @@ void GMainWindow::OnConfigure() {      if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {          render_window->installEventFilter(render_window);          render_window->setAttribute(Qt::WA_Hover, true); -        mouse_hide_timer.start();      } else {          render_window->removeEventFilter(render_window);          render_window->setAttribute(Qt::WA_Hover, false);      } +    if (UISettings::values.hide_mouse) { +        mouse_hide_timer.start(); +    } +      UpdateStatusButtons();  } @@ -2615,8 +2621,7 @@ void GMainWindow::UpdateUISettings() {  }  void GMainWindow::HideMouseCursor() { -    if (emu_thread == nullptr || -        (!UISettings::values.hide_mouse && !Settings::values.mouse_panning)) { +    if (emu_thread == nullptr && UISettings::values.hide_mouse) {          mouse_hide_timer.stop();          ShowMouseCursor();          return; @@ -2626,8 +2631,7 @@ void GMainWindow::HideMouseCursor() {  void GMainWindow::ShowMouseCursor() {      render_window->unsetCursor(); -    if (emu_thread != nullptr && -        (UISettings::values.hide_mouse || Settings::values.mouse_panning)) { +    if (emu_thread != nullptr && UISettings::values.hide_mouse) {          mouse_hide_timer.start();      }  } @@ -3058,6 +3062,14 @@ int main(int argc, char* argv[]) {      chdir(bin_path.c_str());  #endif +#ifdef __linux__ +    // Set the DISPLAY variable in order to open web browsers +    // TODO (lat9nq): Find a better solution for AppImages to start external applications +    if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { +        qputenv("DISPLAY", ":0"); +    } +#endif +      // Enables the core to make the qt created contexts current on std::threads      QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);      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) {      input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);  } +MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { +    switch (button) { +    case SDL_BUTTON_LEFT: +        return MouseInput::MouseButton::Left; +    case SDL_BUTTON_RIGHT: +        return MouseInput::MouseButton::Right; +    case SDL_BUTTON_MIDDLE: +        return MouseInput::MouseButton::Wheel; +    case SDL_BUTTON_X1: +        return MouseInput::MouseButton::Backward; +    case SDL_BUTTON_X2: +        return MouseInput::MouseButton::Forward; +    default: +        return MouseInput::MouseButton::Undefined; +    } +} +  void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { +    const auto mouse_button = SDLButtonToMouseButton(button);      if (button == SDL_BUTTON_LEFT) {          if (state == SDL_PRESSED) {              TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);          } else {              TouchReleased(0);          } -    } else if (button == SDL_BUTTON_RIGHT) { +    } else {          if (state == SDL_PRESSED) { -            input_subsystem->GetMouse()->PressButton(x, y, button); +            input_subsystem->GetMouse()->PressButton(x, y, mouse_button);          } else { -            input_subsystem->GetMouse()->ReleaseButton(button); +            input_subsystem->GetMouse()->ReleaseButton(mouse_button);          }      }  } 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 {  class InputSubsystem;  } +namespace MouseInput { +enum class MouseButton; +} +  class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {  public:      explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); @@ -42,6 +46,9 @@ protected:      /// Called by WaitEvent when the mouse moves.      void OnMouseMotion(s32 x, s32 y); +    /// Converts a SDL mouse button into MouseInput mouse button +    MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const; +      /// Called by WaitEvent when a mouse button is pressed or released      void OnMouseButton(u32 button, u8 state, s32 x, s32 y);  | 
