diff --git a/CMakeLists.txt b/CMakeLists.txt index d7df28117..6d6dfb89d 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 da28b48b8..6fe38233f 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -143,6 +143,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 3a2ce2f5b..e9b67b99f 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 cb41a664a..18676cbdf 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -8,7 +8,6 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" -#include "core/file_sys/fs.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/orbis_error.h" @@ -152,7 +151,8 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u const VAddr in_addr = reinterpret_cast(*addr); const auto map_flags = static_cast(flags); - s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment); + s32 result = memory->MapMemory(addr, in_addr, len, Core::MemoryProt::NoAccess, map_flags, + Core::VMAType::Reserved, "anon", false, -1, alignment); if (result == 0) { LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); } @@ -263,13 +263,26 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** return memory->QueryProtection(std::bit_cast(addr), start, end, prot); } -int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) { +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); } -int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) { +s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) { + s32 result = sceKernelMprotect(addr, size, prot); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; +} + +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); @@ -344,7 +357,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn break; } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: { - result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection); + result = sceKernelMprotect(entries[i].start, entries[i].length, entries[i].protection); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, entries[i].operation, entries[i].length, result); break; @@ -359,7 +372,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn break; } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: { - result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type, + result = sceKernelMtypeprotect(entries[i].start, entries[i].length, entries[i].type, entries[i].protection); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, entries[i].operation, entries[i].length, result); @@ -380,7 +393,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn return result; } -s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) { +s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name) { if (name == nullptr) { LOG_ERROR(Kernel_Vmm, "name is invalid!"); return ORBIS_KERNEL_ERROR_EFAULT; @@ -396,8 +409,8 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons return ORBIS_OK; } -s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, - size_t alignment, u64* physAddrOut) { +s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, + u64* physAddrOut) { if (searchStart < 0 || searchEnd <= searchStart) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -439,10 +452,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_ return ORBIS_OK; } -s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, - void** addrOut) { - LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", - fmt::ptr(addrIn), len, alignment, flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags, + void** addr_out) { + LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", + fmt::ptr(addr_in), len, alignment, flags); if (len == 0 || !Common::Is2MBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); @@ -456,14 +469,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali } auto* memory = Core::Memory::Instance(); - const VAddr in_addr = reinterpret_cast(addrIn); + const VAddr in_addr = reinterpret_cast(addr_in); const auto map_flags = static_cast(flags); - memory->PoolReserve(addrOut, in_addr, len, map_flags, alignment); + u64 map_alignment = alignment == 0 ? 2_MB : alignment; - return ORBIS_OK; + return memory->MapMemory(addr_out, std::bit_cast(addr_in), len, + Core::MemoryProt::NoAccess, map_flags, Core::VMAType::PoolReserved, + "anon", false, -1, map_alignment); } -s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) { +s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -482,7 +497,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int return memory->PoolCommit(in_addr, len, mem_prot); } -s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) { +s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -523,12 +538,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* break; } case OrbisKernelMemoryPoolOpcode::Protect: { - result = sceKernelMProtect(entry.protect_params.addr, entry.protect_params.len, + result = sceKernelMprotect(entry.protect_params.addr, entry.protect_params.len, entry.protect_params.prot); break; } case OrbisKernelMemoryPoolOpcode::TypeProtect: { - result = sceKernelMTypeProtect( + result = sceKernelMtypeprotect( entry.type_protect_params.addr, entry.type_protect_params.len, entry.type_protect_params.type, entry.type_protect_params.prot); break; @@ -553,30 +568,48 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* return result; } -int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, - void** res) { - LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", - fmt::ptr(addr), len, prot, flags, fd, offset); - auto* h = Common::Singleton::Instance(); +void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) { + LOG_INFO(Kernel_Vmm, + "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}", + fmt::ptr(addr), len, prot, flags, fd, phys_addr); + + void* addr_out; auto* memory = Core::Memory::Instance(); const auto mem_prot = static_cast(prot); const auto mem_flags = static_cast(flags); + + s32 result = ORBIS_OK; if (fd == -1) { - return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags, - Core::VMAType::Flexible); + result = memory->MapMemory(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags, + Core::VMAType::Flexible); } else { - const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); - return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle, - offset); + result = memory->MapFile(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags, + fd, phys_addr); } + + if (result != ORBIS_OK) { + // If the memory mappings fail, mmap sets errno to the appropriate error code, + // then returns (void*)-1; + ErrSceToPosix(result); + return reinterpret_cast(-1); + } + + return addr_out; } -void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { - void* ptr; - LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); - int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr); - ASSERT(result == 0); - return ptr; +s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr, + void** res) { + void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr); + + if (addr_out == reinterpret_cast(-1)) { + // posix_mmap failed, calculate and return the appropriate kernel error code using errno. + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + + // Set the outputted address + *res = addr_out; + return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { @@ -678,8 +711,9 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, sceKernelConfiguredFlexibleMemorySize); - LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); - LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); + LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMprotect); + LIB_FUNCTION("YQOfxL4QfeU", "libScePosix", 1, "libkernel", 1, 1, posix_mprotect); + LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMtypeprotect); // Memory pool LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h index 92e158a00..6cefe0d07 100644 --- a/src/core/libraries/kernel/memory.h +++ b/src/core/libraries/kernel/memory.h @@ -147,9 +147,9 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int flags); int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot); -int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot); +s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot); -int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot); +s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot); int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize); @@ -165,14 +165,14 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries, int* numEntriesOut, int flags); -s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name); +s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name); -s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, - size_t alignment, u64* physAddrOut); -s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, - void** addrOut); -s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags); -s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, + u64* physAddrOut); +s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags, + void** addr_out); +s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, s32* num_processed, s32 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 ca6a0d6cd..ba3640877 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/config.h" #include "common/debug.h" +#include "core/file_sys/fs.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/process.h" @@ -67,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; @@ -181,6 +182,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, auto& area = CarveDmemArea(mapping_start, size)->second; area.memory_type = memory_type; area.is_free = false; + MergeAdjacent(dmem_map, dmem_area); return mapping_start; } @@ -214,90 +216,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { MergeAdjacent(dmem_map, dmem_area); } -int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, - MemoryMapFlags flags, u64 alignment) { - std::scoped_lock lk{mutex}; - alignment = alignment > 0 ? alignment : 2_MB; - VAddr min_address = Common::AlignUp(impl.SystemManagedVirtualBase(), alignment); - VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment); - - // Fixed mapping means the virtual address must exactly match the provided one. - if (True(flags & MemoryMapFlags::Fixed)) { - // Make sure we're mapping to a valid address - mapped_addr = mapped_addr > min_address ? mapped_addr : min_address; - auto vma = FindVMA(mapped_addr)->second; - size_t remaining_size = vma.base + vma.size - mapped_addr; - // If the VMA is mapped or there's not enough space, unmap the region first. - if (vma.IsMapped() || remaining_size < size) { - UnmapMemoryImpl(mapped_addr, size); - vma = FindVMA(mapped_addr)->second; - } - } - - if (False(flags & MemoryMapFlags::Fixed)) { - // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0, - // search from address 0x200000000 instead. - mapped_addr = mapped_addr == 0 ? 0x200000000 : mapped_addr; - mapped_addr = SearchFree(mapped_addr, size, alignment); - if (mapped_addr == -1) { - // No suitable memory areas to map to - return ORBIS_KERNEL_ERROR_ENOMEM; - } - } - - // Add virtual memory area - const auto new_vma_handle = CarveVMA(mapped_addr, size); - auto& new_vma = new_vma_handle->second; - new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); - new_vma.prot = MemoryProt::NoAccess; - new_vma.name = "anon"; - new_vma.type = VMAType::PoolReserved; - - *out_addr = std::bit_cast(mapped_addr); - return ORBIS_OK; -} - -int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, - u64 alignment) { - std::scoped_lock lk{mutex}; - - virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; - alignment = alignment > 0 ? alignment : 16_KB; - VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr; - - // Fixed mapping means the virtual address must exactly match the provided one. - if (True(flags & MemoryMapFlags::Fixed)) { - auto vma = FindVMA(mapped_addr)->second; - size_t remaining_size = vma.base + vma.size - mapped_addr; - // If the VMA is mapped or there's not enough space, unmap the region first. - if (vma.IsMapped() || remaining_size < size) { - UnmapMemoryImpl(mapped_addr, size); - vma = FindVMA(mapped_addr)->second; - } - } - - // Find the first free area starting with provided virtual address. - if (False(flags & MemoryMapFlags::Fixed)) { - mapped_addr = SearchFree(mapped_addr, size, alignment); - if (mapped_addr == -1) { - // No suitable memory areas to map to - return ORBIS_KERNEL_ERROR_ENOMEM; - } - } - - // Add virtual memory area - const auto new_vma_handle = CarveVMA(mapped_addr, size); - auto& new_vma = new_vma_handle->second; - new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); - new_vma.prot = MemoryProt::NoAccess; - new_vma.name = "anon"; - new_vma.type = VMAType::Reserved; - MergeAdjacent(vma_map, new_vma_handle); - - *out_addr = std::bit_cast(mapped_addr); - return ORBIS_OK; -} - int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) { std::scoped_lock lk{mutex}; @@ -351,7 +269,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) return ORBIS_OK; } -int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, +s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name, bool is_exec, PAddr phys_addr, u64 alignment) { std::scoped_lock lk{mutex}; @@ -366,17 +284,18 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; // Fixed mapping means the virtual address must exactly match the provided one. - if (True(flags & MemoryMapFlags::Fixed)) { + // On a PS4, the Fixed flag is ignored if address 0 is provided. + if (True(flags & MemoryMapFlags::Fixed) && virtual_addr != 0) { auto vma = FindVMA(mapped_addr)->second; - size_t remaining_size = vma.base + vma.size - mapped_addr; // There's a possible edge case where we're mapping to a partially reserved range. // To account for this, unmap any reserved areas within this mapping range first. auto unmap_addr = mapped_addr; auto unmap_size = size; + // If flag NoOverwrite is provided, don't overwrite mapped VMAs. // When it isn't provided, VMAs can be overwritten regardless of if they're mapped. while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) && - unmap_addr < mapped_addr + size && remaining_size < size) { + unmap_addr < mapped_addr + size) { auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size); unmap_addr += unmapped; unmap_size -= unmapped; @@ -384,51 +303,65 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M } vma = FindVMA(mapped_addr)->second; - remaining_size = vma.base + vma.size - mapped_addr; + auto remaining_size = vma.base + vma.size - mapped_addr; if (vma.IsMapped() || remaining_size < size) { LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr); return ORBIS_KERNEL_ERROR_ENOMEM; } - } - - // Find the first free area starting with provided virtual address. - if (False(flags & MemoryMapFlags::Fixed)) { - // Provided address needs to be aligned before we can map. + } else { + // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0, + // search from address 0x200000000 instead. alignment = alignment > 0 ? alignment : 16_KB; - mapped_addr = SearchFree(Common::AlignUp(mapped_addr, alignment), size, alignment); + mapped_addr = virtual_addr == 0 ? 0x200000000 : mapped_addr; + mapped_addr = SearchFree(mapped_addr, size, alignment); if (mapped_addr == -1) { // No suitable memory areas to map to return ORBIS_KERNEL_ERROR_ENOMEM; } } - // Perform the mapping. - *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); - TRACK_ALLOC(*out_addr, size, "VMEM"); + // Create a memory area representing this mapping. + const auto new_vma_handle = CarveVMA(mapped_addr, size); + auto& new_vma = new_vma_handle->second; - auto& new_vma = CarveVMA(mapped_addr, size)->second; - new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); - new_vma.prot = prot; - new_vma.name = name; - new_vma.type = type; - new_vma.is_exec = is_exec; - - if (type == VMAType::Direct) { - new_vma.phys_base = phys_addr; - } + // If type is Flexible, we need to track how much flexible memory is used here. if (type == VMAType::Flexible) { flexible_usage += size; } - if (IsValidGpuMapping(mapped_addr, size)) { - rasterizer->MapMemory(mapped_addr, size); + new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); + new_vma.prot = prot; + new_vma.name = name; + new_vma.type = type; + new_vma.phys_base = phys_addr == -1 ? 0 : phys_addr; + new_vma.is_exec = is_exec; + + if (type == VMAType::Reserved) { + // Technically this should be done for direct and flexible mappings too, + // But some Windows-specific limitations make that hard to accomplish. + MergeAdjacent(vma_map, new_vma_handle); } + 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 { + // 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); + } + + TRACK_ALLOC(*out_addr, size, "VMEM"); return ORBIS_OK; } -int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, - MemoryMapFlags flags, uintptr_t fd, size_t offset) { +s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, + MemoryMapFlags flags, s32 fd, s64 phys_addr) { + auto* h = Common::Singleton::Instance(); + VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; const size_t size_aligned = Common::AlignUp(size, 16_KB); @@ -449,8 +382,19 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); } - // Map the file. - impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast(prot), fd); + // Get the file to map + auto file = h->GetFile(fd); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + const auto handle = file->f.GetFileMapping(); + + impl.MapFile(mapped_addr, size_aligned, phys_addr, std::bit_cast(prot), handle); + + if (prot >= MemoryProt::GpuRead) { + ASSERT_MSG(false, "Files cannot be mapped to GPU memory"); + } // Add virtual memory area auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; @@ -485,14 +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 (IsValidGpuMapping(virtual_addr, size)) { - 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; @@ -528,18 +473,16 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma const auto adjusted_size = vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size; const bool has_backing = type == VMAType::Direct || type == VMAType::File; + const auto prot = vma_base.prot; if (type == VMAType::Free) { return adjusted_size; } + if (type == VMAType::Flexible) { flexible_usage -= adjusted_size; } - if (IsValidGpuMapping(virtual_addr, adjusted_size)) { - rasterizer->UnmapMemory(virtual_addr, adjusted_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; @@ -552,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); @@ -605,20 +553,8 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size; if (vma_base.type == VMAType::Free) { - LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region"); - return ORBIS_KERNEL_ERROR_EINVAL; - } - - // 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; + // On PS4, protecting freed memory does nothing. + return adjusted_size; } // Change protection @@ -650,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", @@ -667,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; } @@ -798,12 +748,31 @@ s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) { return ORBIS_OK; } -void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) { - auto it = FindVMA(virtual_addr); +void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) { + // Sizes are aligned up to the nearest 16_KB + auto aligned_size = Common::AlignUp(size, 16_KB); + // Addresses are aligned down to the nearest 16_KB + auto aligned_addr = Common::AlignDown(virtual_addr, 16_KB); - ASSERT_MSG(it->second.Contains(virtual_addr, size), - "Range provided is not fully contained in vma"); - it->second.name = name; + auto it = FindVMA(aligned_addr); + s64 remaining_size = aligned_size; + auto current_addr = aligned_addr; + while (remaining_size > 0) { + // Nothing needs to be done to free VMAs + if (!it->second.IsFree()) { + if (remaining_size < it->second.size) { + // We should split VMAs here, but this could cause trouble for Windows. + // Instead log a warning and name the whole VMA. + // it = CarveVMA(current_addr, remaining_size); + LOG_WARNING(Kernel_Vmm, "Trying to partially name a range"); + } + auto& vma = it->second; + vma.name = name; + } + remaining_size -= it->second.size; + current_addr += it->second.size; + it = FindVMA(current_addr); + } } void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const { @@ -824,6 +793,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds", virtual_addr); + // Align up the virtual_addr first. + virtual_addr = Common::AlignUp(virtual_addr, alignment); auto it = FindVMA(virtual_addr); // If the VMA is free and contains the requested mapping we are done. diff --git a/src/core/memory.h b/src/core/memory.h index 883b48854..b3ebe3c27 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -183,20 +183,14 @@ public: void Free(PAddr phys_addr, size_t size); - int PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, - u64 alignment = 0); - - int Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, - u64 alignment = 0); - int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot); - int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, + s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name = "anon", bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0); - int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, - MemoryMapFlags flags, uintptr_t fd, size_t offset); + s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, + MemoryMapFlags flags, s32 fd, s64 phys_addr); s32 PoolDecommit(VAddr virtual_addr, size_t size); @@ -221,7 +215,7 @@ public: s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type); - void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name); + void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name); void InvalidateMemory(VAddr addr, u64 size) const; diff --git a/src/emulator.cpp b/src/emulator.cpp index 5b93df993..902d185f4 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 @@ -64,8 +65,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")) { @@ -116,6 +122,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); @@ -260,7 +263,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/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 20b78e869..37e8a0973 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -1032,7 +1032,6 @@ void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) { m_instruction.control.mimg = *reinterpret_cast(&hexInstruction); m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode); - ASSERT(m_instruction.control.mimg.r128 == 0); } void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) { diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index e49f95d9a..5675adf3c 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -380,7 +380,7 @@ T Translator::GetSrc64(const InstOperand& operand) { break; case OperandField::VccLo: if constexpr (is_float) { - UNREACHABLE(); + value = ir.PackDouble2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi())); } else { value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi())); } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 68d5e8dc8..7b4b03f27 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -183,6 +183,7 @@ public: void V_READFIRSTLANE_B32(const GcnInst& inst); void V_CVT_I32_F64(const GcnInst& inst); void V_CVT_F64_I32(const GcnInst& inst); + void V_CVT_F64_U32(const GcnInst& inst); void V_CVT_F32_I32(const GcnInst& inst); void V_CVT_F32_U32(const GcnInst& inst); void V_CVT_U32_F32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 6171cca07..fb3f52c7f 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -110,6 +110,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_CVT_I32_F64(inst); case Opcode::V_CVT_F64_I32: return V_CVT_F64_I32(inst); + case Opcode::V_CVT_F64_U32: + return V_CVT_F64_U32(inst); case Opcode::V_CVT_F32_I32: return V_CVT_F32_I32(inst); case Opcode::V_CVT_F32_U32: @@ -684,6 +686,11 @@ void Translator::V_CVT_F64_I32(const GcnInst& inst) { SetDst64(inst.dst[0], ir.ConvertSToF(64, 32, src0)); } +void Translator::V_CVT_F64_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + SetDst64(inst.dst[0], ir.ConvertUToF(64, 32, src0)); +} + void Translator::V_CVT_F32_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; SetDst(inst.dst[0], ir.ConvertSToF(32, 32, src0)); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 5639bc56a..5c972c607 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -152,6 +152,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { // Image gather operations case Opcode::IMAGE_GATHER4: + case Opcode::IMAGE_GATHER4_L: case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_O: @@ -377,6 +378,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); for (u32 i = 0; i < 4; i++) { @@ -426,6 +428,7 @@ void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) { IR::TextureInstInfo info{}; info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info); @@ -451,6 +454,7 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { IR::TextureInstInfo info{}; info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value value = ir.GetVectorReg(val_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg); @@ -509,6 +513,7 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal info.has_lod.Assign(flags.any(MimgModifier::Lod)); info.is_array.Assign(mimg.da); info.is_unnormalized.Assign(mimg.unrm); + info.is_r128.Assign(mimg.r128); if (gather) { info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); @@ -617,6 +622,7 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) { IR::TextureInstInfo info{}; info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value body = ir.CompositeConstruct( diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index d349d7827..24e0741c1 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -84,6 +84,7 @@ struct ImageResource { bool is_atomic{}; bool is_array{}; bool is_written{}; + bool is_r128{}; [[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; @@ -293,7 +294,13 @@ constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexce } constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept { - const auto image = info.ReadUdSharp(sharp_idx); + AmdGpu::Image image{0}; + if (!is_r128) { + image = info.ReadUdSharp(sharp_idx); + } else { + AmdGpu::Buffer buf = info.ReadUdSharp(sharp_idx); + memcpy(&image, &buf, sizeof(buf)); + } if (!image.Valid()) { // Fall back to null image if unbound. return AmdGpu::Image::Null(); diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index cc0bf83d3..18c77e600 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -411,6 +411,7 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& .is_atomic = IsImageAtomicInstruction(inst), .is_array = bool(inst_info.is_array), .is_written = is_written, + .is_r128 = bool(inst_info.is_r128), }); IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 622190cf0..82aa436a7 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -44,6 +44,7 @@ union TextureInstInfo { BitField<9, 1, u32> is_array; BitField<10, 1, u32> is_unnormalized; BitField<11, 1, u32> is_gather; + BitField<12, 1, u32> is_r128; }; union BufferInstInfo { 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) {