diff --git a/src/common/config.cpp b/src/common/config.cpp index e79652b32..94d8b488c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -1310,6 +1310,7 @@ hotkey_pause = f9 hotkey_reload_inputs = f8 hotkey_toggle_mouse_to_joystick = f7 hotkey_toggle_mouse_to_gyro = f6 +hotkey_toggle_mouse_to_touchpad = delete hotkey_quit = lctrl, lshift, end )"; } diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 776792041..2ebfbd244 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -6,8 +6,8 @@ #include "common/elf_info.h" #include "common/logging/log.h" #include "core/libraries/fiber/fiber_error.h" -#include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" +#include "core/tls.h" namespace Libraries::Fiber { @@ -20,7 +20,7 @@ static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef; static std::atomic context_size_check = false; OrbisFiberContext* GetFiberContext() { - return Libraries::Kernel::g_curthread->tcb->tcb_fiber; + return Core::GetTcbBase()->tcb_fiber; } extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp"); @@ -269,7 +269,7 @@ s32 PS4_SYSV_ABI sceFiberRunImpl(OrbisFiber* fiber, void* addr_context, u64 size return ORBIS_FIBER_ERROR_INVALID; } - Core::Tcb* tcb = Libraries::Kernel::g_curthread->tcb; + Core::Tcb* tcb = Core::GetTcbBase(); if (tcb->tcb_fiber) { return ORBIS_FIBER_ERROR_PERMISSION; } diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 02da041c3..e88446e02 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -42,6 +42,16 @@ s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) { } s32 PS4_SYSV_ABI sceKernelGetCpumode() { + LOG_DEBUG(Lib_Kernel, "called"); + auto& attrs = Common::ElfInfo::Instance().GetPSFAttributes(); + u32 is_cpu6 = attrs.six_cpu_mode.Value(); + u32 is_cpu7 = attrs.seven_cpu_mode.Value(); + if (is_cpu6 == 1 && is_cpu7 == 1) { + return 2; + } + if (is_cpu7 == 1) { + return 5; + } return 0; } diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 8ab8b72c3..6c11eebc2 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -663,6 +663,10 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create); + LIB_FUNCTION("lZzFeSxPl08", "libkernel", 1, "libkernel", posix_pthread_setcancelstate); + LIB_FUNCTION("CBNtXOoef-E", "libkernel", 1, "libkernel", posix_sched_get_priority_max); + LIB_FUNCTION("m0iS6jNsXds", "libkernel", 1, "libkernel", posix_sched_get_priority_min); + LIB_FUNCTION("Xs9hdiD7sAA", "libkernel", 1, "libkernel", posix_pthread_setschedparam); LIB_FUNCTION("+U1R4WtXvoc", "libkernel", 1, "libkernel", posix_pthread_detach); LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal); LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join); diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 1ae48dfed..ebb10db68 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -712,8 +712,61 @@ int PS4_SYSV_ABI sceHttpUriCopy() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriEscape() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpUriEscape(char* out, u64* require, u64 prepare, const char* in) { + LOG_TRACE(Lib_Http, "called"); + + if (!in) { + LOG_ERROR(Lib_Http, "Invalid input string"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + auto IsUnreserved = [](unsigned char c) -> bool { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || + c == '-' || c == '_' || c == '.' || c == '~'; + }; + + u64 needed = 0; + const char* src = in; + while (*src) { + unsigned char c = static_cast(*src); + if (IsUnreserved(c)) { + needed++; + } else { + needed += 3; // %XX format + } + src++; + } + needed++; // null terminator + + if (require) { + *require = needed; + } + + if (!out) { + return ORBIS_OK; + } + + if (prepare < needed) { + LOG_ERROR(Lib_Http, "Buffer too small: need {} but only {} available", needed, prepare); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + static const char hex_chars[] = "0123456789ABCDEF"; + src = in; + char* dst = out; + while (*src) { + unsigned char c = static_cast(*src); + if (IsUnreserved(c)) { + *dst++ = *src; + } else { + *dst++ = '%'; + *dst++ = hex_chars[(c >> 4) & 0x0F]; + *dst++ = hex_chars[c & 0x0F]; + } + src++; + } + *dst = '\0'; + return ORBIS_OK; } @@ -1072,12 +1125,163 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v } int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize) { - LOG_ERROR(Lib_Http, "(STUBBED) called"); + LOG_TRACE(Lib_Http, "called"); + + if (!dst || !src) { + LOG_ERROR(Lib_Http, "Invalid parameters"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + if (srcSize == 0) { + dst[0] = '\0'; + return ORBIS_OK; + } + + u64 len = 0; + while (len < srcSize && src[len] != '\0') { + len++; + } + + for (u64 i = 0; i < len; i++) { + dst[i] = src[i]; + } + dst[len] = '\0'; + + char* read = dst; + char* write = dst; + + while (*read) { + if (read[0] == '.' && read[1] == '.' && read[2] == '/') { + read += 3; + continue; + } + + if (read[0] == '.' && read[1] == '/') { + read += 2; + continue; + } + + if (read[0] == '/' && read[1] == '.' && read[2] == '/') { + read += 2; + continue; + } + + if (read[0] == '/' && read[1] == '.' && read[2] == '\0') { + if (write == dst) { + *write++ = '/'; + } + break; + } + + bool is_dotdot_mid = (read[0] == '/' && read[1] == '.' && read[2] == '.' && read[3] == '/'); + bool is_dotdot_end = + (read[0] == '/' && read[1] == '.' && read[2] == '.' && read[3] == '\0'); + + if (is_dotdot_mid || is_dotdot_end) { + if (write > dst) { + if (*(write - 1) == '/') { + write--; + } + while (write > dst && *(write - 1) != '/') { + write--; + } + + if (is_dotdot_mid && write > dst) { + write--; + } + } + + if (is_dotdot_mid) { + read += 3; + } else { + break; + } + continue; + } + + if ((read[0] == '.' && read[1] == '\0') || + (read[0] == '.' && read[1] == '.' && read[2] == '\0')) { + break; + } + + if (read[0] == '/') { + *write++ = *read++; + } + while (*read && *read != '/') { + *write++ = *read++; + } + } + + *write = '\0'; return ORBIS_OK; } int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) { - LOG_ERROR(Lib_Http, "(STUBBED) called"); + LOG_TRACE(Lib_Http, "called"); + + if (!in) { + LOG_ERROR(Lib_Http, "Invalid input string"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + // Locale-independent hex digit check + auto IsHex = [](char c) -> bool { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + }; + + // Convert hex char to int value + auto HexToInt = [](char c) -> int { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return 0; + }; + + // Check for valid percent-encoded sequence (%XX) + auto IsValidPercentSequence = [&](const char* s) -> bool { + return s[0] == '%' && s[1] != '\0' && s[2] != '\0' && IsHex(s[1]) && IsHex(s[2]); + }; + + u64 needed = 0; + const char* src = in; + while (*src) { + if (IsValidPercentSequence(src)) { + src += 3; + } else { + src++; + } + needed++; + } + needed++; // null terminator + + if (require) { + *require = needed; + } + + if (!out) { + return ORBIS_OK; + } + + if (prepare < needed) { + LOG_ERROR(Lib_Http, "Buffer too small: need {} but only {} available", needed, prepare); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + src = in; + char* dst = out; + while (*src) { + if (IsValidPercentSequence(src)) { + *dst++ = static_cast((HexToInt(src[1]) << 4) | HexToInt(src[2])); + src += 3; + } else { + *dst++ = *src++; + } + } + *dst = '\0'; + return ORBIS_OK; } diff --git a/src/core/libraries/network/http.h b/src/core/libraries/network/http.h index 701bb0e05..2ad5e171f 100644 --- a/src/core/libraries/network/http.h +++ b/src/core/libraries/network/http.h @@ -148,7 +148,7 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll(); int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare, const OrbisHttpUriElement* srcElement, u32 option); int PS4_SYSV_ABI sceHttpUriCopy(); -int PS4_SYSV_ABI sceHttpUriEscape(); +int PS4_SYSV_ABI sceHttpUriEscape(char* out, u64* require, u64 prepare, const char* in); int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require, u64 prepare, u32 option); int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, diff --git a/src/core/libraries/np/np_commerce.cpp b/src/core/libraries/np/np_commerce.cpp index 1e8440ec0..99b03384a 100644 --- a/src/core/libraries/np/np_commerce.cpp +++ b/src/core/libraries/np/np_commerce.cpp @@ -4,46 +4,87 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/system/commondialog.h" namespace Libraries::Np::NpCommerce { + +using CommonDialog::Error; +using CommonDialog::Result; +using CommonDialog::Status; + +static Status g_dialog_status = Status::NONE; +static Result g_dialog_result = Result::OK; + s32 PS4_SYSV_ABI sceNpCommerceDialogClose() { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); + LOG_INFO(Lib_NpCommerce, "called"); + if (g_dialog_status == Status::NONE) { + return static_cast(Error::NOT_INITIALIZED); + } + if (g_dialog_status != Status::FINISHED) { + return static_cast(Error::NOT_FINISHED); + } + g_dialog_status = Status::INITIALIZED; return ORBIS_OK; } s32 PS4_SYSV_ABI sceNpCommerceDialogGetResult(s32* result) { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); + LOG_INFO(Lib_NpCommerce, "called"); + if (result == nullptr) { + return static_cast(Error::ARG_NULL); + } + if (g_dialog_status != Status::FINISHED) { + return static_cast(Error::NOT_FINISHED); + } + *result = static_cast(g_dialog_result); return ORBIS_OK; } s8 PS4_SYSV_ABI sceNpCommerceDialogGetStatus() { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); - return ORBIS_OK; + LOG_DEBUG(Lib_NpCommerce, "called, status = {}", static_cast(g_dialog_status)); + return static_cast(g_dialog_status); } s32 PS4_SYSV_ABI sceNpCommerceDialogInitialize() { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); + LOG_INFO(Lib_NpCommerce, "called"); + if (g_dialog_status != Status::NONE) { + return static_cast(Error::ALREADY_INITIALIZED); + } + g_dialog_status = Status::INITIALIZED; return ORBIS_OK; } s32 PS4_SYSV_ABI sceNpCommerceDialogInitializeInternal() { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); - return ORBIS_OK; + LOG_INFO(Lib_NpCommerce, "called"); + return sceNpCommerceDialogInitialize(); } s16 PS4_SYSV_ABI sceNpCommerceDialogOpen(s64 check) { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); + LOG_INFO(Lib_NpCommerce, "called, check = {}", check); + if (g_dialog_status != Status::INITIALIZED) { + LOG_WARNING(Lib_NpCommerce, "Dialog not initialized"); + return ORBIS_OK; + } + + g_dialog_status = Status::FINISHED; + g_dialog_result = Result::USER_CANCELED; return ORBIS_OK; } s32 PS4_SYSV_ABI sceNpCommerceDialogTerminate() { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); + LOG_INFO(Lib_NpCommerce, "called"); + if (g_dialog_status == Status::NONE) { + return static_cast(Error::NOT_INITIALIZED); + } + if (g_dialog_status == Status::RUNNING) { + return static_cast(Error::NOT_FINISHED); + } + g_dialog_status = Status::NONE; return ORBIS_OK; } s32 PS4_SYSV_ABI sceNpCommerceDialogUpdateStatus() { - LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); - return ORBIS_OK; + LOG_DEBUG(Lib_NpCommerce, "called, status = {}", static_cast(g_dialog_status)); + return static_cast(g_dialog_status); } s32 PS4_SYSV_ABI sceNpCommerceHidePsStoreIcon() { diff --git a/src/core/linker.cpp b/src/core/linker.cpp index b7c9a2895..ac6b37769 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -368,7 +368,7 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul void* Linker::TlsGetAddr(u64 module_index, u64 offset) { std::scoped_lock lk{mutex}; - DtvEntry* dtv_table = Libraries::Kernel::g_curthread->tcb->tcb_dtv; + DtvEntry* dtv_table = GetTcbBase()->tcb_dtv; if (dtv_table[0].counter != dtv_generation_counter) { // Generation counter changed, a dynamic module was either loaded or unloaded. const u32 old_num_dtvs = dtv_table[1].counter; @@ -381,7 +381,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { delete[] dtv_table; // Update TCB pointer. - Libraries::Kernel::g_curthread->tcb->tcb_dtv = new_dtv_table; + GetTcbBase()->tcb_dtv = new_dtv_table; dtv_table = new_dtv_table; } diff --git a/src/core/tls.cpp b/src/core/tls.cpp index bcefd6f25..57ed20f38 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -46,6 +46,10 @@ void SetTcbBase(void* image_address) { ASSERT(result != 0); } +Tcb* GetTcbBase() { + return reinterpret_cast(TlsGetValue(GetTcbKey())); +} + #elif defined(__APPLE__) && defined(ARCH_X86_64) // Apple x86_64 @@ -145,6 +149,12 @@ void SetTcbBase(void* image_address) { "Failed to store thread LDT page pointer: {}", errno); } +Tcb* GetTcbBase() { + Tcb* tcb; + asm volatile("mov %%fs:0x0, %0" : "=r"(tcb)); + return tcb; +} + #elif defined(ARCH_X86_64) // Other POSIX x86_64 @@ -154,6 +164,10 @@ void SetTcbBase(void* image_address) { ASSERT_MSG(ret == 0, "Failed to set GS base: errno {}", errno); } +Tcb* GetTcbBase() { + return Libraries::Kernel::g_curthread->tcb; +} + #else // POSIX non-x86_64 @@ -176,6 +190,10 @@ void SetTcbBase(void* image_address) { ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0); } +Tcb* GetTcbBase() { + return static_cast(pthread_getspecific(GetTcbKey())); +} + #endif thread_local std::once_flag init_tls_flag; diff --git a/src/core/tls.h b/src/core/tls.h index 0ae512a04..83940be7a 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -39,6 +39,9 @@ u32 GetTcbKey(); /// Sets the data pointer to the TCB block. void SetTcbBase(void* image_address); +/// Retrieves Tcb structure for the calling thread. +Tcb* GetTcbBase(); + /// Makes sure TLS is initialized for the thread before entering guest. void EnsureThreadInitialized(); diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index d38b45ddd..e74569737 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -106,6 +106,7 @@ auto output_array = std::array{ ControllerOutput(HOTKEY_RELOAD_INPUTS), ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK), ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_GYRO), + ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_TOUCHPAD), ControllerOutput(HOTKEY_RENDERDOC), ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID), @@ -579,6 +580,9 @@ void ControllerOutput::FinalizeUpdate() { case HOTKEY_TOGGLE_MOUSE_TO_GYRO: PushSDLEvent(SDL_EVENT_MOUSE_TO_GYRO); break; + case HOTKEY_TOGGLE_MOUSE_TO_TOUCHPAD: + PushSDLEvent(SDL_EVENT_MOUSE_TO_TOUCHPAD); + break; case HOTKEY_RENDERDOC: PushSDLEvent(SDL_EVENT_RDOC_CAPTURE); break; @@ -773,6 +777,9 @@ void ActivateOutputsFromInputs() { it.ResetUpdate(); } + // Check for input blockers + ApplyMouseInputBlockers(); + // Iterate over all inputs, and update their respecive outputs accordingly for (auto& it : connections) { it.output->AddUpdate(it.ProcessBinding()); diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 0d95d1c4a..eaadd164e 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -34,9 +34,10 @@ #define SDL_EVENT_RELOAD_INPUTS SDL_EVENT_USER + 5 #define SDL_EVENT_MOUSE_TO_JOYSTICK SDL_EVENT_USER + 6 #define SDL_EVENT_MOUSE_TO_GYRO SDL_EVENT_USER + 7 -#define SDL_EVENT_RDOC_CAPTURE SDL_EVENT_USER + 8 -#define SDL_EVENT_QUIT_DIALOG SDL_EVENT_USER + 9 -#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 10 +#define SDL_EVENT_MOUSE_TO_TOUCHPAD SDL_EVENT_USER + 8 +#define SDL_EVENT_RDOC_CAPTURE SDL_EVENT_USER + 9 +#define SDL_EVENT_QUIT_DIALOG SDL_EVENT_USER + 10 +#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 11 #define LEFTJOYSTICK_HALFMODE 0x00010000 #define RIGHTJOYSTICK_HALFMODE 0x00020000 @@ -52,7 +53,8 @@ #define HOTKEY_RELOAD_INPUTS 0xf0000005 #define HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK 0xf0000006 #define HOTKEY_TOGGLE_MOUSE_TO_GYRO 0xf0000007 -#define HOTKEY_RENDERDOC 0xf0000008 +#define HOTKEY_TOGGLE_MOUSE_TO_TOUCHPAD 0xf0000008 +#define HOTKEY_RENDERDOC 0xf0000009 #define SDL_UNMAPPED UINT32_MAX - 1 @@ -141,6 +143,7 @@ const std::map string_to_cbutton_map = { {"hotkey_reload_inputs", HOTKEY_RELOAD_INPUTS}, {"hotkey_toggle_mouse_to_joystick", HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK}, {"hotkey_toggle_mouse_to_gyro", HOTKEY_TOGGLE_MOUSE_TO_GYRO}, + {"hotkey_toggle_mouse_to_touchpad", HOTKEY_TOGGLE_MOUSE_TO_TOUCHPAD}, {"hotkey_renderdoc_capture", HOTKEY_RENDERDOC}, }; diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp index 3c718dbd5..cbb07721b 100644 --- a/src/input/input_mouse.cpp +++ b/src/input/input_mouse.cpp @@ -6,12 +6,19 @@ #include "common/assert.h" #include "common/types.h" #include "input/controller.h" +#include "input/input_handler.h" #include "input_mouse.h" +#include +#include #include "SDL3/SDL.h" +extern Frontend::WindowSDL* g_window; + namespace Input { +extern std::list> pressed_keys; + int mouse_joystick_binding = 0; float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250; bool mouse_gyro_roll_mode = false; @@ -80,7 +87,6 @@ void EmulateJoystick(GameController* controller, u32 interval) { constexpr float constant_down_accel[3] = {0.0f, 10.0f, 0.0f}; void EmulateGyro(GameController* controller, u32 interval) { - // LOG_INFO(Input, "todo gyro"); float d_x = 0, d_y = 0; SDL_GetRelativeMouseState(&d_x, &d_y); controller->Acceleration(1, constant_down_accel); @@ -92,6 +98,31 @@ void EmulateGyro(GameController* controller, u32 interval) { controller->Gyro(1, gyro_from_mouse); } +void EmulateTouchpad(GameController* controller, u32 interval) { + float x, y; + SDL_MouseButtonFlags mouse_buttons = SDL_GetMouseState(&x, &y); + controller->SetTouchpadState(0, (mouse_buttons & SDL_BUTTON_LMASK) != 0, + std::clamp(x / g_window->GetWidth(), 0.0f, 1.0f), + std::clamp(y / g_window->GetHeight(), 0.0f, 1.0f)); + controller->CheckButton(0, Libraries::Pad::OrbisPadButtonDataOffset::TouchPad, + (mouse_buttons & SDL_BUTTON_RMASK) != 0); +} + +void ApplyMouseInputBlockers() { + switch (mouse_mode) { + case MouseMode::Touchpad: + for (auto& k : pressed_keys) { + if (k.first.input.sdl_id == SDL_BUTTON_LEFT || + k.first.input.sdl_id == SDL_BUTTON_RIGHT) { + k.second = true; + } + } + break; + default: + break; + } +} + Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) { auto* controller = (GameController*)param; switch (mouse_mode) { @@ -101,6 +132,9 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) { case MouseMode::Gyro: EmulateGyro(controller, interval); break; + case MouseMode::Touchpad: + EmulateTouchpad(controller, interval); + break; default: break; diff --git a/src/input/input_mouse.h b/src/input/input_mouse.h index a56ef2d8f..da1d874ec 100644 --- a/src/input/input_mouse.h +++ b/src/input/input_mouse.h @@ -12,6 +12,7 @@ enum MouseMode { Off = 0, Joystick, Gyro, + Touchpad, }; bool ToggleMouseModeTo(MouseMode m); @@ -22,6 +23,8 @@ void SetMouseGyroRollMode(bool mode); void EmulateJoystick(GameController* controller, u32 interval); void EmulateGyro(GameController* controller, u32 interval); +void ApplyMouseInputBlockers(); + // Polls the mouse for changes Uint32 MousePolling(void* param, Uint32 id, Uint32 interval); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 449defdd1..476a56b52 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -457,6 +457,11 @@ void WindowSDL::WaitEvent() { SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(), Input::ToggleMouseModeTo(Input::MouseMode::Gyro)); break; + case SDL_EVENT_MOUSE_TO_TOUCHPAD: + SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(), + Input::ToggleMouseModeTo(Input::MouseMode::Touchpad)); + SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(), false); + break; case SDL_EVENT_RDOC_CAPTURE: VideoCore::TriggerCapture(); break; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index cc8f6956d..fee0b408e 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/debug.h" +#include "common/thread.h" #include "imgui/renderer/texture_manager.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -17,6 +18,8 @@ Scheduler::Scheduler(const Instance& instance) profiler_scope = reinterpret_cast(std::malloc(sizeof(tracy::VkCtxScope))); #endif AllocateWorkerCommandBuffers(); + priority_pending_ops_thread = + std::jthread(std::bind_front(&Scheduler::PriorityPendingOpsThread, this)); } Scheduler::~Scheduler() { @@ -167,6 +170,32 @@ void Scheduler::SubmitExecution(SubmitInfo& info) { PopPendingOperations(); } +void Scheduler::PriorityPendingOpsThread(std::stop_token stoken) { + Common::SetCurrentThreadName("shadPS4:GpuSchedPriorityPendingOpsRunner"); + + while (!stoken.stop_requested()) { + PendingOp op; + { + std::unique_lock lk(priority_pending_ops_mutex); + priority_pending_ops_cv.wait(lk, stoken, + [this] { return !priority_pending_ops.empty(); }); + if (stoken.stop_requested()) { + break; + } + + op = std::move(priority_pending_ops.front()); + priority_pending_ops.pop(); + } + + master_semaphore.Wait(op.gpu_tick); + if (stoken.stop_requested()) { + break; + } + + op.callback(); + } +} + void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf) { if (dirty_state.viewports) { dirty_state.viewports = false; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 506b84159..aff299e54 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "common/unique_function.h" @@ -401,10 +402,21 @@ public: } /// Defers an operation until the gpu has reached the current cpu tick. + /// Will be run when submitting or calling PopPendingOperations. void DeferOperation(Common::UniqueFunction&& func) { pending_ops.emplace(std::move(func), CurrentTick()); } + /// Defers an operation until the gpu has reached the current cpu tick. + /// Runs as soon as possible in another thread. + void DeferPriorityOperation(Common::UniqueFunction&& func) { + { + std::unique_lock lk(priority_pending_ops_mutex); + priority_pending_ops.emplace(std::move(func), CurrentTick()); + } + priority_pending_ops_cv.notify_one(); + } + static std::mutex submit_mutex; private: @@ -412,6 +424,8 @@ private: void SubmitExecution(SubmitInfo& info); + void PriorityPendingOpsThread(std::stop_token stoken); + private: const Instance& instance; MasterSemaphore master_semaphore; @@ -424,6 +438,10 @@ private: u64 gpu_tick; }; std::queue pending_ops; + std::queue priority_pending_ops; + std::mutex priority_pending_ops_mutex; + std::condition_variable_any priority_pending_ops_cv; + std::jthread priority_pending_ops_thread; RenderState render_state; bool is_rendering = false; tracy::VkCtxScope* profiler_scope{}; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index c7604995a..17c7e67b3 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -52,9 +52,6 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& std::max(std::min(device_local_memory - min_vacancy_critical, min_spacing_critical), DEFAULT_CRITICAL_GC_MEMORY)); trigger_gc_memory = static_cast((device_local_memory - mem_threshold) / 2); - - downloaded_images_thread = - std::jthread([&](const std::stop_token& token) { DownloadedImagesThread(token); }); } TextureCache::~TextureCache() = default; @@ -125,33 +122,11 @@ void TextureCache::DownloadImageMemory(ImageId image_id) { cmdbuf.copyImageToBuffer(image.GetImage(), vk::ImageLayout::eTransferSrcOptimal, download_buffer.Handle(), image_download); - { - std::unique_lock lock(downloaded_images_mutex); - downloaded_images_queue.emplace(scheduler.CurrentTick(), image.info.guest_address, download, - download_size); - downloaded_images_cv.notify_one(); - } -} - -void TextureCache::DownloadedImagesThread(const std::stop_token& token) { - auto* memory = Core::Memory::Instance(); - while (!token.stop_requested()) { - DownloadedImage image; - { - std::unique_lock lock{downloaded_images_mutex}; - downloaded_images_cv.wait(lock, token, - [this] { return !downloaded_images_queue.empty(); }); - if (token.stop_requested()) { - break; - } - image = downloaded_images_queue.front(); - downloaded_images_queue.pop(); - } - - scheduler.GetMasterSemaphore()->Wait(image.tick); - memory->TryWriteBacking(std::bit_cast(image.device_addr), image.download, - image.download_size); - } + scheduler.DeferPriorityOperation( + [this, device_addr = image.info.guest_address, download, download_size] { + Core::Memory::Instance()->TryWriteBacking(std::bit_cast(device_addr), download, + download_size); + }); } void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 9d25069db..141ac938f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -314,16 +314,6 @@ private: Common::LeastRecentlyUsedCache lru_cache; PageTable page_table; std::mutex mutex; - struct DownloadedImage { - u64 tick; - VAddr device_addr; - void* download; - size_t download_size; - }; - std::queue downloaded_images_queue; - std::mutex downloaded_images_mutex; - std::condition_variable_any downloaded_images_cv; - std::jthread downloaded_images_thread; struct MetaDataInfo { enum class Type { CMask,