Merge branch 'shadps4-emu:main' into rdr2-test

This commit is contained in:
Stephen Miller 2025-12-06 12:15:23 -06:00 committed by GitHub
commit d7883108a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 411 additions and 66 deletions

View File

@ -1310,6 +1310,7 @@ hotkey_pause = f9
hotkey_reload_inputs = f8 hotkey_reload_inputs = f8
hotkey_toggle_mouse_to_joystick = f7 hotkey_toggle_mouse_to_joystick = f7
hotkey_toggle_mouse_to_gyro = f6 hotkey_toggle_mouse_to_gyro = f6
hotkey_toggle_mouse_to_touchpad = delete
hotkey_quit = lctrl, lshift, end hotkey_quit = lctrl, lshift, end
)"; )";
} }

View File

@ -6,8 +6,8 @@
#include "common/elf_info.h" #include "common/elf_info.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/fiber/fiber_error.h" #include "core/libraries/fiber/fiber_error.h"
#include "core/libraries/kernel/threads/pthread.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/tls.h"
namespace Libraries::Fiber { namespace Libraries::Fiber {
@ -20,7 +20,7 @@ static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef;
static std::atomic<u32> context_size_check = false; static std::atomic<u32> context_size_check = false;
OrbisFiberContext* GetFiberContext() { 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"); 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; return ORBIS_FIBER_ERROR_INVALID;
} }
Core::Tcb* tcb = Libraries::Kernel::g_curthread->tcb; Core::Tcb* tcb = Core::GetTcbBase();
if (tcb->tcb_fiber) { if (tcb->tcb_fiber) {
return ORBIS_FIBER_ERROR_PERMISSION; return ORBIS_FIBER_ERROR_PERMISSION;
} }

View File

@ -42,6 +42,16 @@ s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) {
} }
s32 PS4_SYSV_ABI sceKernelGetCpumode() { 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; return 0;
} }

View File

@ -663,6 +663,10 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once); LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once);
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self);
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create); 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("+U1R4WtXvoc", "libkernel", 1, "libkernel", posix_pthread_detach);
LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal); LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal);
LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join); LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join);

View File

@ -712,8 +712,61 @@ int PS4_SYSV_ABI sceHttpUriCopy() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpUriEscape() { int PS4_SYSV_ABI sceHttpUriEscape(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;
}
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<unsigned char>(*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<unsigned char>(*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; 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) { 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; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) { 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<char>((HexToInt(src[1]) << 4) | HexToInt(src[2]));
src += 3;
} else {
*dst++ = *src++;
}
}
*dst = '\0';
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -148,7 +148,7 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll();
int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare, int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare,
const OrbisHttpUriElement* srcElement, u32 option); const OrbisHttpUriElement* srcElement, u32 option);
int PS4_SYSV_ABI sceHttpUriCopy(); 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, int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
u64 prepare, u32 option); u64 prepare, u32 option);
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,

View File

@ -4,46 +4,87 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/system/commondialog.h"
namespace Libraries::Np::NpCommerce { 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() { 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<s32>(Error::NOT_INITIALIZED);
}
if (g_dialog_status != Status::FINISHED) {
return static_cast<s32>(Error::NOT_FINISHED);
}
g_dialog_status = Status::INITIALIZED;
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpCommerceDialogGetResult(s32* result) { s32 PS4_SYSV_ABI sceNpCommerceDialogGetResult(s32* result) {
LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); LOG_INFO(Lib_NpCommerce, "called");
if (result == nullptr) {
return static_cast<s32>(Error::ARG_NULL);
}
if (g_dialog_status != Status::FINISHED) {
return static_cast<s32>(Error::NOT_FINISHED);
}
*result = static_cast<s32>(g_dialog_result);
return ORBIS_OK; return ORBIS_OK;
} }
s8 PS4_SYSV_ABI sceNpCommerceDialogGetStatus() { s8 PS4_SYSV_ABI sceNpCommerceDialogGetStatus() {
LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); LOG_DEBUG(Lib_NpCommerce, "called, status = {}", static_cast<u32>(g_dialog_status));
return ORBIS_OK; return static_cast<s8>(g_dialog_status);
} }
s32 PS4_SYSV_ABI sceNpCommerceDialogInitialize() { 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<s32>(Error::ALREADY_INITIALIZED);
}
g_dialog_status = Status::INITIALIZED;
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpCommerceDialogInitializeInternal() { s32 PS4_SYSV_ABI sceNpCommerceDialogInitializeInternal() {
LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); LOG_INFO(Lib_NpCommerce, "called");
return ORBIS_OK; return sceNpCommerceDialogInitialize();
} }
s16 PS4_SYSV_ABI sceNpCommerceDialogOpen(s64 check) { 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; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpCommerceDialogTerminate() { 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<s32>(Error::NOT_INITIALIZED);
}
if (g_dialog_status == Status::RUNNING) {
return static_cast<s32>(Error::NOT_FINISHED);
}
g_dialog_status = Status::NONE;
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpCommerceDialogUpdateStatus() { s32 PS4_SYSV_ABI sceNpCommerceDialogUpdateStatus() {
LOG_ERROR(Lib_NpCommerce, "(STUBBED) called"); LOG_DEBUG(Lib_NpCommerce, "called, status = {}", static_cast<u32>(g_dialog_status));
return ORBIS_OK; return static_cast<s32>(g_dialog_status);
} }
s32 PS4_SYSV_ABI sceNpCommerceHidePsStoreIcon() { s32 PS4_SYSV_ABI sceNpCommerceHidePsStoreIcon() {

View File

@ -368,7 +368,7 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
void* Linker::TlsGetAddr(u64 module_index, u64 offset) { void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
std::scoped_lock lk{mutex}; 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) { if (dtv_table[0].counter != dtv_generation_counter) {
// Generation counter changed, a dynamic module was either loaded or unloaded. // Generation counter changed, a dynamic module was either loaded or unloaded.
const u32 old_num_dtvs = dtv_table[1].counter; const u32 old_num_dtvs = dtv_table[1].counter;
@ -381,7 +381,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
delete[] dtv_table; delete[] dtv_table;
// Update TCB pointer. // Update TCB pointer.
Libraries::Kernel::g_curthread->tcb->tcb_dtv = new_dtv_table; GetTcbBase()->tcb_dtv = new_dtv_table;
dtv_table = new_dtv_table; dtv_table = new_dtv_table;
} }

View File

@ -46,6 +46,10 @@ void SetTcbBase(void* image_address) {
ASSERT(result != 0); ASSERT(result != 0);
} }
Tcb* GetTcbBase() {
return reinterpret_cast<Tcb*>(TlsGetValue(GetTcbKey()));
}
#elif defined(__APPLE__) && defined(ARCH_X86_64) #elif defined(__APPLE__) && defined(ARCH_X86_64)
// Apple x86_64 // Apple x86_64
@ -145,6 +149,12 @@ void SetTcbBase(void* image_address) {
"Failed to store thread LDT page pointer: {}", errno); "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) #elif defined(ARCH_X86_64)
// Other POSIX 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); ASSERT_MSG(ret == 0, "Failed to set GS base: errno {}", errno);
} }
Tcb* GetTcbBase() {
return Libraries::Kernel::g_curthread->tcb;
}
#else #else
// POSIX non-x86_64 // POSIX non-x86_64
@ -176,6 +190,10 @@ void SetTcbBase(void* image_address) {
ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0); ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0);
} }
Tcb* GetTcbBase() {
return static_cast<Tcb*>(pthread_getspecific(GetTcbKey()));
}
#endif #endif
thread_local std::once_flag init_tls_flag; thread_local std::once_flag init_tls_flag;

View File

@ -39,6 +39,9 @@ u32 GetTcbKey();
/// Sets the data pointer to the TCB block. /// Sets the data pointer to the TCB block.
void SetTcbBase(void* image_address); 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. /// Makes sure TLS is initialized for the thread before entering guest.
void EnsureThreadInitialized(); void EnsureThreadInitialized();

View File

@ -106,6 +106,7 @@ auto output_array = std::array{
ControllerOutput(HOTKEY_RELOAD_INPUTS), ControllerOutput(HOTKEY_RELOAD_INPUTS),
ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK), ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK),
ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_GYRO), ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_GYRO),
ControllerOutput(HOTKEY_TOGGLE_MOUSE_TO_TOUCHPAD),
ControllerOutput(HOTKEY_RENDERDOC), ControllerOutput(HOTKEY_RENDERDOC),
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID), ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID),
@ -579,6 +580,9 @@ void ControllerOutput::FinalizeUpdate() {
case HOTKEY_TOGGLE_MOUSE_TO_GYRO: case HOTKEY_TOGGLE_MOUSE_TO_GYRO:
PushSDLEvent(SDL_EVENT_MOUSE_TO_GYRO); PushSDLEvent(SDL_EVENT_MOUSE_TO_GYRO);
break; break;
case HOTKEY_TOGGLE_MOUSE_TO_TOUCHPAD:
PushSDLEvent(SDL_EVENT_MOUSE_TO_TOUCHPAD);
break;
case HOTKEY_RENDERDOC: case HOTKEY_RENDERDOC:
PushSDLEvent(SDL_EVENT_RDOC_CAPTURE); PushSDLEvent(SDL_EVENT_RDOC_CAPTURE);
break; break;
@ -773,6 +777,9 @@ void ActivateOutputsFromInputs() {
it.ResetUpdate(); it.ResetUpdate();
} }
// Check for input blockers
ApplyMouseInputBlockers();
// Iterate over all inputs, and update their respecive outputs accordingly // Iterate over all inputs, and update their respecive outputs accordingly
for (auto& it : connections) { for (auto& it : connections) {
it.output->AddUpdate(it.ProcessBinding()); it.output->AddUpdate(it.ProcessBinding());

View File

@ -34,9 +34,10 @@
#define SDL_EVENT_RELOAD_INPUTS SDL_EVENT_USER + 5 #define SDL_EVENT_RELOAD_INPUTS SDL_EVENT_USER + 5
#define SDL_EVENT_MOUSE_TO_JOYSTICK SDL_EVENT_USER + 6 #define SDL_EVENT_MOUSE_TO_JOYSTICK SDL_EVENT_USER + 6
#define SDL_EVENT_MOUSE_TO_GYRO SDL_EVENT_USER + 7 #define SDL_EVENT_MOUSE_TO_GYRO SDL_EVENT_USER + 7
#define SDL_EVENT_RDOC_CAPTURE SDL_EVENT_USER + 8 #define SDL_EVENT_MOUSE_TO_TOUCHPAD SDL_EVENT_USER + 8
#define SDL_EVENT_QUIT_DIALOG SDL_EVENT_USER + 9 #define SDL_EVENT_RDOC_CAPTURE SDL_EVENT_USER + 9
#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 10 #define SDL_EVENT_QUIT_DIALOG SDL_EVENT_USER + 10
#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 11
#define LEFTJOYSTICK_HALFMODE 0x00010000 #define LEFTJOYSTICK_HALFMODE 0x00010000
#define RIGHTJOYSTICK_HALFMODE 0x00020000 #define RIGHTJOYSTICK_HALFMODE 0x00020000
@ -52,7 +53,8 @@
#define HOTKEY_RELOAD_INPUTS 0xf0000005 #define HOTKEY_RELOAD_INPUTS 0xf0000005
#define HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK 0xf0000006 #define HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK 0xf0000006
#define HOTKEY_TOGGLE_MOUSE_TO_GYRO 0xf0000007 #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 #define SDL_UNMAPPED UINT32_MAX - 1
@ -141,6 +143,7 @@ const std::map<std::string, u32> string_to_cbutton_map = {
{"hotkey_reload_inputs", HOTKEY_RELOAD_INPUTS}, {"hotkey_reload_inputs", HOTKEY_RELOAD_INPUTS},
{"hotkey_toggle_mouse_to_joystick", HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK}, {"hotkey_toggle_mouse_to_joystick", HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK},
{"hotkey_toggle_mouse_to_gyro", HOTKEY_TOGGLE_MOUSE_TO_GYRO}, {"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}, {"hotkey_renderdoc_capture", HOTKEY_RENDERDOC},
}; };

View File

@ -6,12 +6,19 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/types.h" #include "common/types.h"
#include "input/controller.h" #include "input/controller.h"
#include "input/input_handler.h"
#include "input_mouse.h" #include "input_mouse.h"
#include <common/singleton.h>
#include <emulator.h>
#include "SDL3/SDL.h" #include "SDL3/SDL.h"
extern Frontend::WindowSDL* g_window;
namespace Input { namespace Input {
extern std::list<std::pair<InputEvent, bool>> pressed_keys;
int mouse_joystick_binding = 0; int mouse_joystick_binding = 0;
float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250; float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250;
bool mouse_gyro_roll_mode = false; 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}; constexpr float constant_down_accel[3] = {0.0f, 10.0f, 0.0f};
void EmulateGyro(GameController* controller, u32 interval) { void EmulateGyro(GameController* controller, u32 interval) {
// LOG_INFO(Input, "todo gyro");
float d_x = 0, d_y = 0; float d_x = 0, d_y = 0;
SDL_GetRelativeMouseState(&d_x, &d_y); SDL_GetRelativeMouseState(&d_x, &d_y);
controller->Acceleration(1, constant_down_accel); controller->Acceleration(1, constant_down_accel);
@ -92,6 +98,31 @@ void EmulateGyro(GameController* controller, u32 interval) {
controller->Gyro(1, gyro_from_mouse); 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) { Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
auto* controller = (GameController*)param; auto* controller = (GameController*)param;
switch (mouse_mode) { switch (mouse_mode) {
@ -101,6 +132,9 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
case MouseMode::Gyro: case MouseMode::Gyro:
EmulateGyro(controller, interval); EmulateGyro(controller, interval);
break; break;
case MouseMode::Touchpad:
EmulateTouchpad(controller, interval);
break;
default: default:
break; break;

View File

@ -12,6 +12,7 @@ enum MouseMode {
Off = 0, Off = 0,
Joystick, Joystick,
Gyro, Gyro,
Touchpad,
}; };
bool ToggleMouseModeTo(MouseMode m); bool ToggleMouseModeTo(MouseMode m);
@ -22,6 +23,8 @@ void SetMouseGyroRollMode(bool mode);
void EmulateJoystick(GameController* controller, u32 interval); void EmulateJoystick(GameController* controller, u32 interval);
void EmulateGyro(GameController* controller, u32 interval); void EmulateGyro(GameController* controller, u32 interval);
void ApplyMouseInputBlockers();
// Polls the mouse for changes // Polls the mouse for changes
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval); Uint32 MousePolling(void* param, Uint32 id, Uint32 interval);

View File

@ -457,6 +457,11 @@ void WindowSDL::WaitEvent() {
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(), SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
Input::ToggleMouseModeTo(Input::MouseMode::Gyro)); Input::ToggleMouseModeTo(Input::MouseMode::Gyro));
break; 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: case SDL_EVENT_RDOC_CAPTURE:
VideoCore::TriggerCapture(); VideoCore::TriggerCapture();
break; break;

View File

@ -3,6 +3,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/thread.h"
#include "imgui/renderer/texture_manager.h" #include "imgui/renderer/texture_manager.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
@ -17,6 +18,8 @@ Scheduler::Scheduler(const Instance& instance)
profiler_scope = reinterpret_cast<tracy::VkCtxScope*>(std::malloc(sizeof(tracy::VkCtxScope))); profiler_scope = reinterpret_cast<tracy::VkCtxScope*>(std::malloc(sizeof(tracy::VkCtxScope)));
#endif #endif
AllocateWorkerCommandBuffers(); AllocateWorkerCommandBuffers();
priority_pending_ops_thread =
std::jthread(std::bind_front(&Scheduler::PriorityPendingOpsThread, this));
} }
Scheduler::~Scheduler() { Scheduler::~Scheduler() {
@ -167,6 +170,32 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
PopPendingOperations(); 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) { void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf) {
if (dirty_state.viewports) { if (dirty_state.viewports) {
dirty_state.viewports = false; dirty_state.viewports = false;

View File

@ -5,6 +5,7 @@
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <thread>
#include <queue> #include <queue>
#include "common/unique_function.h" #include "common/unique_function.h"
@ -401,10 +402,21 @@ public:
} }
/// Defers an operation until the gpu has reached the current cpu tick. /// Defers an operation until the gpu has reached the current cpu tick.
/// Will be run when submitting or calling PopPendingOperations.
void DeferOperation(Common::UniqueFunction<void>&& func) { void DeferOperation(Common::UniqueFunction<void>&& func) {
pending_ops.emplace(std::move(func), CurrentTick()); 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<void>&& 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; static std::mutex submit_mutex;
private: private:
@ -412,6 +424,8 @@ private:
void SubmitExecution(SubmitInfo& info); void SubmitExecution(SubmitInfo& info);
void PriorityPendingOpsThread(std::stop_token stoken);
private: private:
const Instance& instance; const Instance& instance;
MasterSemaphore master_semaphore; MasterSemaphore master_semaphore;
@ -424,6 +438,10 @@ private:
u64 gpu_tick; u64 gpu_tick;
}; };
std::queue<PendingOp> pending_ops; std::queue<PendingOp> pending_ops;
std::queue<PendingOp> 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; RenderState render_state;
bool is_rendering = false; bool is_rendering = false;
tracy::VkCtxScope* profiler_scope{}; tracy::VkCtxScope* profiler_scope{};

View File

@ -52,9 +52,6 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler&
std::max<u64>(std::min(device_local_memory - min_vacancy_critical, min_spacing_critical), std::max<u64>(std::min(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_GC_MEMORY)); DEFAULT_CRITICAL_GC_MEMORY));
trigger_gc_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2); trigger_gc_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2);
downloaded_images_thread =
std::jthread([&](const std::stop_token& token) { DownloadedImagesThread(token); });
} }
TextureCache::~TextureCache() = default; TextureCache::~TextureCache() = default;
@ -125,33 +122,11 @@ void TextureCache::DownloadImageMemory(ImageId image_id) {
cmdbuf.copyImageToBuffer(image.GetImage(), vk::ImageLayout::eTransferSrcOptimal, cmdbuf.copyImageToBuffer(image.GetImage(), vk::ImageLayout::eTransferSrcOptimal,
download_buffer.Handle(), image_download); download_buffer.Handle(), image_download);
{ scheduler.DeferPriorityOperation(
std::unique_lock lock(downloaded_images_mutex); [this, device_addr = image.info.guest_address, download, download_size] {
downloaded_images_queue.emplace(scheduler.CurrentTick(), image.info.guest_address, download, Core::Memory::Instance()->TryWriteBacking(std::bit_cast<u8*>(device_addr), download,
download_size); 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<u8*>(image.device_addr), image.download,
image.download_size);
}
} }
void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) {

View File

@ -314,16 +314,6 @@ private:
Common::LeastRecentlyUsedCache<ImageId, u64> lru_cache; Common::LeastRecentlyUsedCache<ImageId, u64> lru_cache;
PageTable page_table; PageTable page_table;
std::mutex mutex; std::mutex mutex;
struct DownloadedImage {
u64 tick;
VAddr device_addr;
void* download;
size_t download_size;
};
std::queue<DownloadedImage> downloaded_images_queue;
std::mutex downloaded_images_mutex;
std::condition_variable_any downloaded_images_cv;
std::jthread downloaded_images_thread;
struct MetaDataInfo { struct MetaDataInfo {
enum class Type { enum class Type {
CMask, CMask,