diff --git a/CMakeLists.txt b/CMakeLists.txt index f85d22b6c..20d33ac95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioin.h + src/core/libraries/voice/voice.cpp + src/core/libraries/voice/voice.h src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout_backend.h diff --git a/documents/building-linux.md b/documents/building-linux.md index cdc8ba12f..bd07b2eff 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \ openssl-devel libevdev-devel libudev-devel libXext-devel \ qt6-qtbase-devel qt6-qtbase-private-devel \ qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \ - vulkan-devel vulkan-validation-layers libpng-devel + vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel ``` #### Arch Linux diff --git a/src/common/config.cpp b/src/common/config.cpp index 6bccd0f37..6565ab82a 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -69,7 +69,7 @@ static bool vkGuestMarkers = false; static bool rdocEnable = false; static bool isFpsColor = true; static bool isSeparateLogFilesEnabled = false; -static s16 cursorState = HideCursorState::Idle; +static int cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; @@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255}; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; +static bool isPSNSignedIn = false; // Gui static bool load_game_size = true; @@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) { showBackgroundImage = show; } +bool getPSNSignedIn() { + return isPSNSignedIn; +} + +void setPSNSignedIn(bool sign) { + isPSNSignedIn = sign; +} + void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) { isNeo = toml::find_or(general, "isPS4Pro", false); isDevKit = toml::find_or(general, "isDevKit", false); + isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", false); playBGM = toml::find_or(general, "playBGM", false); isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); trophyNotificationDuration = @@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) { data["General"]["isPS4Pro"] = isNeo; data["General"]["isDevKit"] = isDevKit; + data["General"]["isPSNSignedIn"] = isPSNSignedIn; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["playBGM"] = playBGM; @@ -1098,6 +1109,7 @@ void setDefaultValues() { isHDRAllowed = false; isNeo = false; isDevKit = false; + isPSNSignedIn = false; isFullscreen = false; isTrophyPopupDisabled = false; playBGM = false; diff --git a/src/common/config.h b/src/common/config.h index aba23621c..404854ae2 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -14,7 +14,7 @@ struct GameInstallDir { bool enabled; }; -enum HideCursorState : s16 { Never, Idle, Always }; +enum HideCursorState : int { Never, Idle, Always }; void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); @@ -39,6 +39,7 @@ bool getCompatibilityEnabled(); bool getCheckCompatibilityOnStartup(); int getBackgroundImageOpacity(); bool getShowBackgroundImage(); +bool getPSNSignedIn(); std::string getLogFilter(); std::string getLogType(); @@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); void setBackgroundImageOpacity(int opacity); void setShowBackgroundImage(bool show); +void setPSNSignedIn(bool sign); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index fd0614e1b..05935fbdc 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -141,6 +141,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Camera) \ SUB(Lib, CompanionHttpd) \ SUB(Lib, CompanionUtil) \ + SUB(Lib, Voice) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index bbfd7455b..1da84b219 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -98,6 +98,7 @@ enum class Class : u8 { Lib_Fiber, ///< The LibSceFiber implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Videodec, ///< The LibSceVideodec implementation. + Lib_Voice, ///< The LibSceVoice implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_Mouse, ///< The LibSceMouse implementation Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 71c4c2d0a..0b113eb31 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/scm_rev.h" namespace Common { @@ -15,5 +17,26 @@ constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@"; constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@"; constexpr char g_scm_date[] = "@BUILD_DATE@"; +const std::string GetRemoteNameFromLink() { + std::string remote_url(Common::g_scm_remote_url); + std::string remote_host; + try { + if (remote_url.starts_with("http")) { + if (*remote_url.rbegin() == '/') { + remote_url.pop_back(); + } + remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); + } else if (remote_url.starts_with("git@")) { + auto after_comma_pos = remote_url.find(':') + 1, slash_pos = remote_url.find('/'); + remote_host = remote_url.substr(after_comma_pos, slash_pos - after_comma_pos); + } else { + remote_host = "unknown"; + } + } catch (...) { + remote_host = "unknown"; + } + return remote_host; +} + } // namespace diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index 36b844e94..2f6d770bb 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h @@ -3,6 +3,8 @@ #pragma once +#include + namespace Common { extern const char g_version[]; @@ -15,4 +17,6 @@ extern const char g_scm_remote_name[]; extern const char g_scm_remote_url[]; extern const char g_scm_date[]; +const std::string GetRemoteNameFromLink(); + } // namespace Common diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 263cf24b8..911ae4cd5 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -145,6 +145,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { if (event.event.ident == ident && event.event.filter == filter) { if (filter == SceKernelEvent::Filter::VideoOut) { event.TriggerDisplay(trigger_data); + } else if (filter == SceKernelEvent::Filter::User) { + event.TriggerUser(trigger_data); } else { event.Trigger(trigger_data); } diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index a0367c66a..e6e3c0c53 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -98,6 +98,12 @@ struct EqueueEvent { event.data = reinterpret_cast(data); } + void TriggerUser(void* data) { + is_triggered = true; + event.fflags++; + event.udata = data; + } + void TriggerDisplay(void* data) { is_triggered = true; if (data != nullptr) { diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index ce694dc1e..18676cbdf 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -264,6 +264,8 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** } s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) { + LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size, + prot); Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryProt protection_flags = static_cast(prot); return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); @@ -279,6 +281,8 @@ s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) { } s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) { + LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size, + prot); Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryProt protection_flags = static_cast(prot); return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 61310bfb5..59b427d22 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize if (thread == nullptr || cpusetp == nullptr) { return POSIX_EINVAL; } + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) { + return ret; + } + auto* attr_ptr = &thread->attr; - return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp); + auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp); + + thread->lock.unlock(); + return ret; } int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize, @@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize if (thread == nullptr || cpusetp == nullptr) { return POSIX_EINVAL; } - auto* attr_ptr = &thread->attr; - if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) { + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) { return ret; } - return thread->SetAffinity(thread->attr.cpuset); + + auto* attr_ptr = &thread->attr; + auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp); + + if (ret == ORBIS_OK) { + ret = thread->SetAffinity(thread->attr.cpuset); + } + + thread->lock.unlock(); + return ret; } int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) { diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp index 71f6438a6..e098b00a4 100644 --- a/src/core/libraries/kernel/threads/pthread_attr.cpp +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { posix_pthread_attr_getdetachstate); LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_setguardsize); + LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getschedparam); // Orbis LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 45b32846f..762c1e762 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -60,6 +60,7 @@ #include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" +#include "core/libraries/voice/voice.h" #include "core/libraries/web_browser_dialog/webbrowserdialog.h" #include "core/libraries/zlib/zlib_sce.h" #include "fiber/fiber.h" @@ -128,6 +129,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Camera::RegisterlibSceCamera(sym); Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym); Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym); + Libraries::Voice::RegisterlibSceVoice(sym); } } // namespace Libraries diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index a60dcd86f..bc920b5a9 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/config.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" @@ -10,6 +11,8 @@ namespace Libraries::NpManager { +#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT) + int PS4_SYSV_ABI Func_EF4378573542A508() { LOG_ERROR(Lib_NpManager, "(STUBBED) called"); return ORBIS_OK; @@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetAccountCountryA() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id, + OrbisNpCountryCode* country_code) { + LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id); + if (country_code == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + ::memset(country_code, 0, sizeof(OrbisNpCountryCode)); + // TODO: get NP country code from config + ::memcpy(country_code->country_code, "us", 2); + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() { @@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) if (online_id == nullptr || account_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - *account_id = 0; - return ORBIS_NP_ERROR_SIGNED_OUT; + *account_id = 0xFEEDFACE; + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { @@ -950,8 +960,8 @@ int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account if (account_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - *account_id = 0; - return ORBIS_NP_ERROR_SIGNED_OUT; + *account_id = 0xFEEDFACE; + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetAccountLanguage() { @@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) if (np_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - return ORBIS_NP_ERROR_SIGNED_OUT; + memset(np_id, 0, sizeof(OrbisNpId)); + strncpy(np_id->handle.data, Config::getUserName().c_str(), sizeof(np_id->handle.data)); + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetNpReachabilityState() { @@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI if (online_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - return ORBIS_NP_ERROR_SIGNED_OUT; + memset(online_id, 0, sizeof(OrbisNpOnlineId)); + strncpy(online_id->data, Config::getUserName().c_str(), sizeof(online_id->data)); + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetParentalControlInfo() { @@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta if (state == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - *state = OrbisNpState::SignedOut; - LOG_DEBUG(Lib_NpManager, "Signed out"); + *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut; + LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out"); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 02a1a32f6..1078a9f3e 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -32,6 +32,12 @@ struct OrbisNpId { u8 reserved[8]; }; +struct OrbisNpCountryCode { + char country_code[2]; + char end; + char pad; +}; + int PS4_SYSV_ABI Func_EF4378573542A508(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool(); @@ -215,7 +221,8 @@ int PS4_SYSV_ABI sceNpCreateRequest(); int PS4_SYSV_ABI sceNpDeleteRequest(int reqId); int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountCountry(); -int PS4_SYSV_ABI sceNpGetAccountCountryA(); +int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id, + OrbisNpCountryCode* country_code); int PS4_SYSV_ABI sceNpGetAccountDateOfBirth(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 5dfc68e90..42582783b 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.z = states[i].angularVelocity.z; pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; - if (engine) { + pData[i].acceleration.x = states[i].acceleration.x * 0.098; + pData[i].acceleration.y = states[i].acceleration.y * 0.098; + pData[i].acceleration.z = states[i].acceleration.z * 0.098; + pData[i].angularVelocity.x = states[i].angularVelocity.x; + pData[i].angularVelocity.y = states[i].angularVelocity.y; + pData[i].angularVelocity.z = states[i].angularVelocity.z; + + if (engine && handle == 1) { const auto gyro_poll_rate = engine->GetAccelPollRate(); if (gyro_poll_rate != 0.0f) { - GameController::CalculateOrientation(pData[i].acceleration, - pData[i].angularVelocity, - 1.0f / gyro_poll_rate, pData[i].orientation); + auto now = std::chrono::steady_clock::now(); + float deltaTime = std::chrono::duration_cast( + now - controller->GetLastUpdate()) + .count() / + 1000000.0f; + controller->SetLastUpdate(now); + Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation(); + Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f}; + GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, + deltaTime, lastOrientation, outputOrientation); + pData[i].orientation = outputOrientation; + controller->SetLastOrientation(outputOrientation); } } + pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); + + if (handle == 1) { + if (controller->GetTouchCount() >= 127) { + controller->SetTouchCount(0); + } + + if (controller->GetSecondaryTouchCount() >= 127) { + controller->SetSecondaryTouchCount(0); + } + + if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) { + controller->SetTouchCount(controller->GetTouchCount() + 1); + controller->SetSecondaryTouchCount(controller->GetTouchCount()); + } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) { + controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1); + } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) { + if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + } else { + if (controller->WasSecondaryTouchReset()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + controller->UnsetSecondaryTouchResetBool(); + } + } + } + + controller->SetPreviousTouchNum(pData->touchData.touchNum); + + if (pData->touchData.touchNum == 1) { + states[i].touchpad[0].ID = controller->GetTouchCount(); + states[i].touchpad[1].ID = 0; + } else if (pData->touchData.touchNum == 2) { + states[i].touchpad[0].ID = controller->GetTouchCount(); + states[i].touchpad[1].ID = controller->GetSecondaryTouchCount(); + } + } else { + states[i].touchpad[0].ID = 1; + states[i].touchpad[1].ID = 2; + } + pData[i].touchData.touch[0].x = states[i].touchpad[0].x; pData[i].touchData.touch[0].y = states[i].touchpad[0].y; - pData[i].touchData.touch[0].id = 1; + pData[i].touchData.touch[0].id = states[i].touchpad[0].ID; pData[i].touchData.touch[1].x = states[i].touchpad[1].x; pData[i].touchData.touch[1].y = states[i].touchpad[1].y; - pData[i].touchData.touch[1].id = 2; + pData[i].touchData.touch[1].id = states[i].touchpad[1].ID; pData[i].connected = connected; pData[i].timestamp = states[i].time; pData[i].connectedCount = connected_count; @@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)]; pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)]; pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)]; + pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)]; pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)]; pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)]; pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)]; - pData->acceleration.x = state.acceleration.x; - pData->acceleration.y = state.acceleration.y; - pData->acceleration.z = state.acceleration.z; + pData->acceleration.x = state.acceleration.x * 0.098; + pData->acceleration.y = state.acceleration.y * 0.098; + pData->acceleration.z = state.acceleration.z * 0.098; pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.z = state.angularVelocity.z; pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f}; - if (engine) { + + // Only do this on handle 1 for now + if (engine && handle == 1) { const auto gyro_poll_rate = engine->GetAccelPollRate(); if (gyro_poll_rate != 0.0f) { + auto now = std::chrono::steady_clock::now(); + float deltaTime = std::chrono::duration_cast( + now - controller->GetLastUpdate()) + .count() / + 1000000.0f; + controller->SetLastUpdate(now); + Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation(); + Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f}; GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, - 1.0f / gyro_poll_rate, pData->orientation); + deltaTime, lastOrientation, outputOrientation); + pData->orientation = outputOrientation; + controller->SetLastOrientation(outputOrientation); } } pData->touchData.touchNum = (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); + + // Only do this on handle 1 for now + if (handle == 1) { + if (controller->GetTouchCount() >= 127) { + controller->SetTouchCount(0); + } + + if (controller->GetSecondaryTouchCount() >= 127) { + controller->SetSecondaryTouchCount(0); + } + + if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) { + controller->SetTouchCount(controller->GetTouchCount() + 1); + controller->SetSecondaryTouchCount(controller->GetTouchCount()); + } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) { + controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1); + } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) { + if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + } else { + if (controller->WasSecondaryTouchReset()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + controller->UnsetSecondaryTouchResetBool(); + } + } + } + + controller->SetPreviousTouchNum(pData->touchData.touchNum); + + if (pData->touchData.touchNum == 1) { + state.touchpad[0].ID = controller->GetTouchCount(); + state.touchpad[1].ID = 0; + } else if (pData->touchData.touchNum == 2) { + state.touchpad[0].ID = controller->GetTouchCount(); + state.touchpad[1].ID = controller->GetSecondaryTouchCount(); + } + } else { + state.touchpad[0].ID = 1; + state.touchpad[1].ID = 2; + } + pData->touchData.touch[0].x = state.touchpad[0].x; pData->touchData.touch[0].y = state.touchpad[0].y; - pData->touchData.touch[0].id = 1; + pData->touchData.touch[0].id = state.touchpad[0].ID; pData->touchData.touch[1].x = state.touchpad[1].x; pData->touchData.touch[1].y = state.touchpad[1].y; - pData->touchData.touch[1].id = 2; + pData->touchData.touch[1].id = state.touchpad[1].ID; pData->timestamp = state.time; pData->connected = true; // isConnected; //TODO fix me proper pData->connectedCount = 1; // connectedCount; diff --git a/src/core/libraries/voice/voice.cpp b/src/core/libraries/voice/voice.cpp new file mode 100644 index 000000000..caa16431a --- /dev/null +++ b/src/core/libraries/voice/voice.cpp @@ -0,0 +1,203 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/voice/voice.h" + +namespace Libraries::Voice { + +s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceCreatePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceDeletePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceEnd() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate) { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + *bitrate = 48000; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetMuteFlag() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetPortAttr() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info) { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + info->port_type = 0; + info->state = 3; + info->byte_count = 0; + info->frame_size = 1; + info->edge_count = 0; + info->reserved = 0; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetResourceInfo() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetVolume() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceInit() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceInitHQ() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoicePausePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoicePausePortAll() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceReadFromOPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceResetPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceResumePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceResumePortAll() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetBitRate() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetMuteFlag() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetThreadsParams() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetVolume() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceStart() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceStop() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceUpdatePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceVADAdjustment() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceVADSetVersion() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceWriteToIPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("oV9GAdJ23Gw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceConnectIPortToOPort); + LIB_FUNCTION("nXpje5yNpaE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceCreatePort); + LIB_FUNCTION("b7kJI+nx2hg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceDeletePort); + LIB_FUNCTION("ajVj3QG2um4", "libSceVoice", 1, "libSceVoice", 0, 0, + sceVoiceDisconnectIPortFromOPort); + LIB_FUNCTION("Oo0S5PH7FIQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceEnd); + LIB_FUNCTION("cJLufzou6bc", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetBitRate); + LIB_FUNCTION("Pc4z1QjForU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetMuteFlag); + LIB_FUNCTION("elcxZTEfHZM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortAttr); + LIB_FUNCTION("CrLqDwWLoXM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortInfo); + LIB_FUNCTION("Z6QV6j7igvE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetResourceInfo); + LIB_FUNCTION("jjkCjneOYSs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetVolume); + LIB_FUNCTION("9TrhuGzberQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInit); + LIB_FUNCTION("IPHvnM5+g04", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInitHQ); + LIB_FUNCTION("x0slGBQW+wY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePort); + LIB_FUNCTION("Dinob0yMRl8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePortAll); + LIB_FUNCTION("cQ6DGsQEjV4", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceReadFromOPort); + LIB_FUNCTION("udAxvCePkUs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResetPort); + LIB_FUNCTION("gAgN+HkiEzY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePort); + LIB_FUNCTION("jbkJFmOZ9U0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePortAll); + LIB_FUNCTION("TexwmOHQsDg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetBitRate); + LIB_FUNCTION("gwUynkEgNFY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlag); + LIB_FUNCTION("oUha0S-Ij9Q", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlagAll); + LIB_FUNCTION("clyKUyi3RYU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetThreadsParams); + LIB_FUNCTION("QBFoAIjJoXQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetVolume); + LIB_FUNCTION("54phPH2LZls", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStart); + LIB_FUNCTION("Ao2YNSA7-Qo", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStop); + LIB_FUNCTION("jSZNP7xJrcw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceUpdatePort); + LIB_FUNCTION("hg9T73LlRiU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADAdjustment); + LIB_FUNCTION("wFeAxEeEi-8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADSetVersion); + LIB_FUNCTION("YeJl6yDlhW0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceWriteToIPort); +}; + +} // namespace Libraries::Voice \ No newline at end of file diff --git a/src/core/libraries/voice/voice.h b/src/core/libraries/voice/voice.h new file mode 100644 index 000000000..8f008f2cc --- /dev/null +++ b/src/core/libraries/voice/voice.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Voice { + +struct OrbisVoicePortInfo { + s32 port_type; + s32 state; + u32* edge; + u32 byte_count; + u32 frame_size; + u16 edge_count; + u16 reserved; +}; + +s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort(); +s32 PS4_SYSV_ABI sceVoiceCreatePort(); +s32 PS4_SYSV_ABI sceVoiceDeletePort(); +s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort(); +s32 PS4_SYSV_ABI sceVoiceEnd(); +s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate); +s32 PS4_SYSV_ABI sceVoiceGetMuteFlag(); +s32 PS4_SYSV_ABI sceVoiceGetPortAttr(); +s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info); +s32 PS4_SYSV_ABI sceVoiceGetResourceInfo(); +s32 PS4_SYSV_ABI sceVoiceGetVolume(); +s32 PS4_SYSV_ABI sceVoiceInit(); +s32 PS4_SYSV_ABI sceVoiceInitHQ(); +s32 PS4_SYSV_ABI sceVoicePausePort(); +s32 PS4_SYSV_ABI sceVoicePausePortAll(); +s32 PS4_SYSV_ABI sceVoiceReadFromOPort(); +s32 PS4_SYSV_ABI sceVoiceResetPort(); +s32 PS4_SYSV_ABI sceVoiceResumePort(); +s32 PS4_SYSV_ABI sceVoiceResumePortAll(); +s32 PS4_SYSV_ABI sceVoiceSetBitRate(); +s32 PS4_SYSV_ABI sceVoiceSetMuteFlag(); +s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll(); +s32 PS4_SYSV_ABI sceVoiceSetThreadsParams(); +s32 PS4_SYSV_ABI sceVoiceSetVolume(); +s32 PS4_SYSV_ABI sceVoiceStart(); +s32 PS4_SYSV_ABI sceVoiceStop(); +s32 PS4_SYSV_ABI sceVoiceUpdatePort(); +s32 PS4_SYSV_ABI sceVoiceVADAdjustment(); +s32 PS4_SYSV_ABI sceVoiceVADSetVersion(); +s32 PS4_SYSV_ABI sceVoiceWriteToIPort(); + +void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Voice \ No newline at end of file diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 3e6d8c22e..c50b03a8f 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -117,6 +117,18 @@ void Linker::Execute(const std::vector args) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); + // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory. + // Some games fail without accurately emulating this behavior. + s64 phys_addr{}; + s32 result = Libraries::Kernel::sceKernelAllocateDirectMemory( + 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr); + if (result == 0) { + void* addr{reinterpret_cast(0xfe0000000)}; + result = Libraries::Kernel::sceKernelMapNamedDirectMemory( + &addr, 0x10000, 0x13, 0, phys_addr, 0x10000, "SceGnmDriver"); + } + ASSERT_MSG(result == 0, "Unable to emulate libSceGnmDriver initialization"); + // Start main module. EntryParams params{}; params.argc = 1; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ab59219b2..ba3640877 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -68,7 +68,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1 } u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { - static constexpr u64 MinSizeToClamp = 512_MB; + static constexpr u64 MinSizeToClamp = 2_MB; // Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer. if (size < MinSizeToClamp) { return size; @@ -262,10 +262,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); TRACK_ALLOC(out_addr, size, "VMEM"); - if (prot >= MemoryProt::GpuRead) { - // PS4s only map to GPU memory when the protection includes GPU access. - // If the address to map to is too high, PS4s throw a page fault and crash. - ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping"); + if (IsValidGpuMapping(mapped_addr, size)) { rasterizer->MapMemory(mapped_addr, size); } @@ -345,19 +342,15 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo MergeAdjacent(vma_map, new_vma_handle); } - if (prot >= MemoryProt::GpuRead) { - // PS4s only map to GPU memory when the protection includes GPU access. - // If the address to map to is too high, PS4s throw a page fault and crash. - ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping"); - rasterizer->MapMemory(mapped_addr, size); - } - if (type == VMAType::Reserved || type == VMAType::PoolReserved) { // For Reserved/PoolReserved mappings, we don't perform any address space allocations. // Just set out_addr to mapped_addr instead. *out_addr = std::bit_cast(mapped_addr); } else { - // Type is either Direct, Flexible, or Code, these need to be mapped in our address space. + // If this is not a reservation, then map to GPU and address space + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); } @@ -429,7 +422,6 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { const bool is_exec = vma_base.is_exec; const auto start_in_vma = virtual_addr - vma_base_addr; const auto type = vma_base.type; - const auto prot = vma_base.prot; if (type != VMAType::PoolReserved && type != VMAType::Pooled) { LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!"); @@ -437,15 +429,15 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { } if (type == VMAType::Pooled) { + // We always map PoolCommitted memory to GPU, so unmap when decomitting. + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } + // Track how much pooled memory is decommitted pool_budget += size; } - if (prot >= MemoryProt::GpuRead) { - // If this mapping has GPU access, unmap from GPU. - rasterizer->UnmapMemory(virtual_addr, size); - } - // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, size); auto& vma = new_it->second; @@ -486,15 +478,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma if (type == VMAType::Free) { return adjusted_size; } + if (type == VMAType::Flexible) { flexible_usage -= adjusted_size; } - if (prot >= MemoryProt::GpuRead) { - // If this mapping has GPU access, unmap from GPU. - rasterizer->UnmapMemory(virtual_addr, size); - } - // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, adjusted_size); auto& vma = new_it->second; @@ -507,6 +495,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma auto& post_merge_vma = post_merge_it->second; bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File; if (type != VMAType::Reserved && type != VMAType::PoolReserved) { + // If this mapping has GPU access, unmap from GPU. + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } + // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, phys_base, is_exec, has_backing, readonly_file); @@ -564,30 +557,6 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s return adjusted_size; } - // Validate protection flags - constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead | - MemoryProt::CpuReadWrite | MemoryProt::GpuRead | - MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; - - MemoryProt invalid_flags = prot & ~valid_flags; - if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) { - LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", - u32(prot), u32(invalid_flags)); - return ORBIS_KERNEL_ERROR_EINVAL; - } - - if (vma_base.prot < MemoryProt::GpuRead && prot >= MemoryProt::GpuRead) { - // New protection will give the GPU access to this VMA, perform a rasterizer map - ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU mapping"); - rasterizer->MapMemory(addr, size); - } - - if (vma_base.prot >= MemoryProt::GpuRead && prot < MemoryProt::GpuRead) { - // New protection will remove the GPU's access to this VMA, perform a rasterizer unmap - ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU unmap"); - rasterizer->UnmapMemory(addr, size); - } - // Change protection vma_base.prot = prot; @@ -617,11 +586,25 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { std::scoped_lock lk{mutex}; - s64 protected_bytes = 0; + // Validate protection flags + constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead | + MemoryProt::CpuReadWrite | MemoryProt::GpuRead | + MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; + + MemoryProt invalid_flags = prot & ~valid_flags; + if (invalid_flags != MemoryProt::NoAccess) { + LOG_ERROR(Kernel_Vmm, "Invalid protection flags"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Align addr and size to the nearest page boundary. auto aligned_addr = Common::AlignDown(addr, 16_KB); auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB); - do { + + // Protect all VMAs between aligned_addr and aligned_addr + aligned_size. + s64 protected_bytes = 0; + while (protected_bytes < aligned_size) { auto it = FindVMA(aligned_addr + protected_bytes); auto& vma_base = it->second; ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds", @@ -634,7 +617,7 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { return result; } protected_bytes += result; - } while (protected_bytes < aligned_size); + } return ORBIS_OK; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 2ad8446ab..bb50b8686 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -62,8 +63,13 @@ Emulator::~Emulator() { Config::saveMainWindow(config_dir / "config.toml"); } -void Emulator::Run(const std::filesystem::path& file, const std::vector args) { +void Emulator::Run(std::filesystem::path file, const std::vector args) { + if (std::filesystem::is_directory(file)) { + file /= "eboot.bin"; + } + const auto eboot_name = file.filename().string(); + auto game_folder = file.parent_path(); if (const auto game_folder_name = game_folder.filename().string(); game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) { @@ -114,6 +120,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); - std::string remote_host; - try { - if (*remote_url.rbegin() == '/') { - remote_url.pop_back(); - } - remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); - } catch (...) { - remote_host = "unknown"; - } + std::string remote_host = Common::GetRemoteNameFromLink(); if (Common::g_is_release) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) { window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title); @@ -258,7 +261,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/" + eboot_name); - linker->LoadModule(eboot_path); + if (linker->LoadModule(eboot_path) == -1) { + LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}", + std::filesystem::absolute(eboot_path).string()); + std::quick_exit(0); + } // check if we have system modules to load LoadSystemModules(game_info.game_serial); diff --git a/src/emulator.h b/src/emulator.h index 08c2807a1..257ccd694 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -25,7 +25,7 @@ public: Emulator(); ~Emulator(); - void Run(const std::filesystem::path& file, const std::vector args = {}); + void Run(std::filesystem::path file, const std::vector args = {}); void UpdatePlayTime(const std::string& serial); private: diff --git a/src/input/controller.cpp b/src/input/controller.cpp index bb8db9a7c..42cabb837 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) { AddState(state); } -// Stolen from -// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs -float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms -const float Kp = 50.0f; // Proportional gain -const float Ki = 1.0f; // Integral gain -Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0}; void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, + Libraries::Pad::OrbisFQuaternion& lastOrientation, Libraries::Pad::OrbisFQuaternion& orientation) { - float ax = acceleration.x, ay = acceleration.y, az = acceleration.z; - float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z; + Libraries::Pad::OrbisFQuaternion q = lastOrientation; + Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z, + 0.0f}; - float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z; + Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y, + q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z, + q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x, + q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z}; - // Normalize accelerometer measurement - float norm = std::sqrt(ax * ax + ay * ay + az * az); - if (norm == 0.0f || deltaTime == 0.0f) - return; // Handle NaN - norm = 1.0f / norm; - ax *= norm; - ay *= norm; - az *= norm; + Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w}; - // Estimated direction of gravity - float vx = 2.0f * (q2 * q4 - q1 * q3); - float vy = 2.0f * (q1 * q2 + q3 * q4); - float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; + q.x += qDot.x * deltaTime; + q.y += qDot.y * deltaTime; + q.z += qDot.z * deltaTime; + q.w += qDot.w * deltaTime; - // Error is cross product between estimated direction and measured direction of gravity - float ex = (ay * vz - az * vy); - float ey = (az * vx - ax * vz); - float ez = (ax * vy - ay * vx); - if (Ki > 0.0f) { - eInt[0] += ex * deltaTime; // Accumulate integral error - eInt[1] += ey * deltaTime; - eInt[2] += ez * deltaTime; - } else { - eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up - } + float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + q.x /= norm; + q.y /= norm; + q.z /= norm; + q.w /= norm; - // Apply feedback terms - gx += Kp * ex + Ki * eInt[0]; - gy += Kp * ey + Ki * eInt[1]; - gz += Kp * ez + Ki * eInt[2]; - - //// Integrate rate of change of quaternion - q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime); - q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime); - q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime); - q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime); - - // Normalize quaternion - norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); - norm = 1.0f / norm; - orientation.w = q1 * norm; - orientation.x = q2 * norm; - orientation.y = q3 * norm; - orientation.z = q4 * norm; - o.w = q1 * norm; - o.x = q2 * norm; - o.y = q3 * norm; - o.z = q4 * norm; + orientation.x = q.x; + orientation.y = q.y; + orientation.z = q.z; + orientation.w = q.w; LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x, orientation.y, orientation.z, orientation.w); } @@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f } } +u8 GameController::GetTouchCount() { + std::scoped_lock lock{m_mutex}; + return m_touch_count; +} + +void GameController::SetTouchCount(u8 touchCount) { + std::scoped_lock lock{m_mutex}; + m_touch_count = touchCount; +} + +u8 GameController::GetSecondaryTouchCount() { + std::scoped_lock lock{m_mutex}; + return m_secondary_touch_count; +} + +void GameController::SetSecondaryTouchCount(u8 touchCount) { + std::scoped_lock lock{m_mutex}; + m_secondary_touch_count = touchCount; + if (touchCount == 0) { + m_was_secondary_reset = true; + } +} + +u8 GameController::GetPreviousTouchNum() { + std::scoped_lock lock{m_mutex}; + return m_previous_touchnum; +} + +void GameController::SetPreviousTouchNum(u8 touchNum) { + std::scoped_lock lock{m_mutex}; + m_previous_touchnum = touchNum; +} + +bool GameController::WasSecondaryTouchReset() { + std::scoped_lock lock{m_mutex}; + return m_was_secondary_reset; +} + +void GameController::UnsetSecondaryTouchResetBool() { + std::scoped_lock lock{m_mutex}; + m_was_secondary_reset = false; +} + +void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) { + std::scoped_lock lock{m_mutex}; + m_orientation = orientation; +} + +Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() { + std::scoped_lock lock{m_mutex}; + return m_orientation; +} + +std::chrono::steady_clock::time_point GameController::GetLastUpdate() { + std::scoped_lock lock{m_mutex}; + return m_last_update; +} + +void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) { + std::scoped_lock lock{m_mutex}; + m_last_update = lastUpdate; +} + void GameController::SetEngine(std::unique_ptr engine) { std::scoped_lock _{m_mutex}; m_engine = std::move(engine); diff --git a/src/input/controller.h b/src/input/controller.h index bbaed75ea..f427a55ec 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -23,6 +23,7 @@ enum class Axis { }; struct TouchpadEntry { + u8 ID = 0; bool state{}; u16 x{}; u16 y{}; @@ -82,9 +83,23 @@ public: Engine* GetEngine(); u32 Poll(); + u8 GetTouchCount(); + void SetTouchCount(u8 touchCount); + u8 GetSecondaryTouchCount(); + void SetSecondaryTouchCount(u8 touchCount); + u8 GetPreviousTouchNum(); + void SetPreviousTouchNum(u8 touchNum); + bool WasSecondaryTouchReset(); + void UnsetSecondaryTouchResetBool(); + + void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation); + Libraries::Pad::OrbisFQuaternion GetLastOrientation(); + std::chrono::steady_clock::time_point GetLastUpdate(); + void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate); static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, + Libraries::Pad::OrbisFQuaternion& lastOrientation, Libraries::Pad::OrbisFQuaternion& orientation); private: @@ -98,8 +113,15 @@ private: int m_connected_count = 0; u32 m_states_num = 0; u32 m_first_state = 0; + u8 m_touch_count = 0; + u8 m_secondary_touch_count = 0; + u8 m_previous_touch_count = 0; + u8 m_previous_touchnum = 0; + bool m_was_secondary_reset = false; std::array m_states; std::array m_private; + std::chrono::steady_clock::time_point m_last_update = {}; + Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f}; std::unique_ptr m_engine = nullptr; }; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 1966aa52b..906a3066e 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -56,15 +56,7 @@ bool MainWindow::Init() { setMinimumSize(720, 405); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); - std::string remote_host; - try { - if (*remote_url.rbegin() == '/') { - remote_url.pop_back(); - } - remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); - } catch (...) { - remote_host = "unknown"; - } + std::string remote_host = Common::GetRemoteNameFromLink(); if (Common::g_is_release) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) { window_title = fmt::format("shadPS4 v{}", Common::g_version); @@ -380,16 +372,15 @@ void MainWindow::CreateConnects() { connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable); connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable); connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); - connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable); connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons); connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen); connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { if (isTableList) { m_game_list_frame->icon_size = - 36 + value; // 36 is the minimum icon size to use due to text disappearing. - m_game_list_frame->ResizeIcons(36 + value); - Config::setIconSize(36 + value); + 48 + value; // 48 is the minimum icon size to use due to text disappearing. + m_game_list_frame->ResizeIcons(48 + value); + Config::setIconSize(48 + value); Config::setSliderPosition(value); } else { m_game_grid_frame->icon_size = 69 + value; diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index a5ec08d36..97c56433d 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -29,7 +29,6 @@ class MainWindow : public QMainWindow { Q_OBJECT signals: void WindowResized(QResizeEvent* event); - void ExtractionFinished(); public: explicit MainWindow(QWidget* parent = nullptr); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a62141099..245e34d35 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -608,6 +608,16 @@ struct Liverpool { } }; + struct BorderColorBufferBase { + u32 base_addr_lo; + BitField<0, 8, u32> base_addr_hi; + + template + T Address() const { + return std::bit_cast(u64(base_addr_hi) << 40 | u64(base_addr_lo) << 8); + } + }; + struct IndexBufferBase { BitField<0, 8, u32> base_addr_hi; u32 base_addr_lo; @@ -1299,7 +1309,9 @@ struct Liverpool { Scissor screen_scissor; INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2); DepthBuffer depth_buffer; - INSERT_PADDING_WORDS(0xA080 - 0xA018); + INSERT_PADDING_WORDS(8); + BorderColorBufferBase ta_bc_base; + INSERT_PADDING_WORDS(0xA080 - 0xA020 - 2); WindowOffset window_offset; ViewportScissor window_scissor; INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2); @@ -1626,6 +1638,7 @@ static_assert(GFX6_3D_REG_INDEX(depth_htile_data_base) == 0xA005); static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C); static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010); static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017); +static_assert(GFX6_3D_REG_INDEX(ta_bc_base) == 0xA020); static_assert(GFX6_3D_REG_INDEX(window_offset) == 0xA080); static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081); static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4bdb08bf2..dff4e5a5f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -716,7 +716,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); } } - const auto vk_sampler = texture_cache.GetSampler(ssharp); + const auto vk_sampler = texture_cache.GetSampler(ssharp, liverpool->regs.ta_bc_base); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); set_writes.push_back({ .dstSet = VK_NULL_HANDLE, diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp index 8dbdd2912..e18c79a59 100644 --- a/src/video_core/texture_cache/sampler.cpp +++ b/src/video_core/texture_cache/sampler.cpp @@ -9,7 +9,8 @@ namespace VideoCore { -Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) { +Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) { if (sampler.force_degamma) { LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); } @@ -20,7 +21,33 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample const float maxAnisotropy = anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()) : 1.0f; + auto borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type); + if (!instance.IsCustomBorderColorSupported()) { + LOG_WARNING(Render_Vulkan, "Custom border color is not supported, falling back to black"); + borderColor = vk::BorderColor::eFloatOpaqueBlack; + } + + const auto customColor = [&]() -> std::optional { + if (borderColor == vk::BorderColor::eFloatCustomEXT) { + const auto borderColorIndex = sampler.border_color_ptr.Value(); + const auto borderColorBuffer = border_color_base.Address*>(); + const auto customBorderColorArray = borderColorBuffer[borderColorIndex]; + + const vk::SamplerCustomBorderColorCreateInfoEXT ret{ + .customBorderColor = + vk::ClearColorValue{ + .float32 = customBorderColorArray, + }, + .format = vk::Format::eR32G32B32A32Sfloat, + }; + return ret; + } else { + return std::nullopt; + } + }(); + const vk::SamplerCreateInfo sampler_ci = { + .pNext = customColor ? &*customColor : nullptr, .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), .minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter), .mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter), @@ -34,7 +61,7 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample .compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func), .minLod = sampler.MinLod(), .maxLod = sampler.MaxLod(), - .borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type), + .borderColor = borderColor, .unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations. }; auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); diff --git a/src/video_core/texture_cache/sampler.h b/src/video_core/texture_cache/sampler.h index 856d39763..28ba0f67b 100644 --- a/src/video_core/texture_cache/sampler.h +++ b/src/video_core/texture_cache/sampler.h @@ -14,7 +14,8 @@ namespace VideoCore { class Sampler { public: - explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler); + explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base); ~Sampler(); Sampler(const Sampler&) = delete; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index ddb4ea799..63cfc4431 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -636,9 +636,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule image.flags &= ~ImageFlagBits::Dirty; } -vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler) { +vk::Sampler TextureCache::GetSampler( + const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) { const u64 hash = XXH3_64bits(&sampler, sizeof(sampler)); - const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler); + const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler, border_color_base); return it->second.Handle(); } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b6bf88958..ccfeb36b2 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -139,7 +139,9 @@ public: void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr); /// Retrieves the sampler that matches the provided S# descriptor. - [[nodiscard]] vk::Sampler GetSampler(const AmdGpu::Sampler& sampler); + [[nodiscard]] vk::Sampler GetSampler( + const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base); /// Retrieves the image with the specified id. [[nodiscard]] Image& GetImage(ImageId id) {