From 6cdc52cdde23a7bf35c6f316217155c04e99418f Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Thu, 29 May 2025 10:56:03 -0500
Subject: [PATCH 01/31] Core: More Memory Cleanup & Fixes (#2997)
* Only perform GPU memory mapping when GPU can access it
This better aligns with hardware observations, and should also speed up unmaps and decommits, since they don't need to be compared with the GPU max address anymore.
* Reserve fixes
ReserveVirtualRange seems to follow the 0x200000000 base address like MemoryPoolReserve does.
Both also need checks in their flags Fixed path to ensure we're mapping in-bounds. If we're not in mapping to our address space, we'll end up reserving and returning the wrong address, which could lead to weird memory issues in games.
I'll need to test on real hardware to verify if such changes are appropriate.
* Better sceKernelMmap
Handles errors where we would previously throw exceptions. Also moves the file logic to MapFile, since that way all the possible errors are in one place.
Also fixes some function parameters to align with our current standards.
* Major refactor
MapDirectMemory, MapFlexibleMemory, ReserveVirtualRange, and MemoryPoolReserve all internally use mmap to perform their mappings. Naturally, this means that all functions have similar behaviors, and a lot of duplicate code.
This add necessary conditional behavior to MapMemory so MemoryPoolReserve and ReserveVirtualRange can use it, without disrupting the behavior of MapDirectMemory or MapFlexibleMemory calls.
* Accurate phys_addr for non-direct mappings
* Properly handle GPU access rights
Since my first commit restricts GPU mappings to memory areas with GPU access permissions, we also need to be updating the GPU mappings appropriately during Protect calls too.
* Update memory.cpp
* Update memory.h
* Update memory.cpp
* Update memory.cpp
* Update memory.cpp
* Revert "Update memory.cpp"
This reverts commit 2c55d014c0efbdfadee4121b01e1dcf5af60e63d.
* Coalesce dmem map
Aligns with hardware observations, hopefully shouldn't break anything since nothing should change hardware-wise when release dmem calls and unmap calls are performed?
Either that or Windows breaks because Windows, will need to test.
* Implement posix_mprotect
Unity calls this
Also fixes the names of sceKernelMprotect and sceKernelMtypeprotect, though that's more of a style change and can be reverted if requested.
* Fix sceKernelSetVirtualRangeName
Partially addresses a "regression" introduced when I fixed up some asserts.
As noted in the code, this implementation is still slightly inaccurate, as handling this properly could cause regressions on Windows.
* Unconditional assert in MapFile
* Remove protect warning
This is expected behavior, shouldn't need any logging.
* Respect alignment
Forgot to properly do this when updating ReserveVirtualRange and MemoryPoolReserve
* Fix Mprotect on free memory
On real hardware, this just does nothing. If something did get protected, there's no way to query that information.
Therefore, it seems pretty safe to just behave like munmap and return size here.
* Minor tidy-up
No functional difference, but looks better.
---
src/core/libraries/kernel/memory.cpp | 106 +++++++-----
src/core/libraries/kernel/memory.h | 18 +-
src/core/memory.cpp | 236 +++++++++++++--------------
src/core/memory.h | 14 +-
4 files changed, 193 insertions(+), 181 deletions(-)
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index cb41a664a..ce694dc1e 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,22 @@ 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) {
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) {
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 +353,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 +368,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 +389,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 +405,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 +448,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 +465,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 +493,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 +534,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 +564,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 +707,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/memory.cpp b/src/core/memory.cpp
index ca6a0d6cd..ab59219b2 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"
@@ -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};
@@ -344,14 +262,17 @@ 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 (IsValidGpuMapping(mapped_addr, size)) {
+ 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);
}
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 +287,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 +306,69 @@ 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)) {
+ 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 (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.
+ *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 +389,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;
@@ -478,6 +429,7 @@ 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!");
@@ -489,7 +441,8 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
pool_budget += size;
}
- if (IsValidGpuMapping(virtual_addr, size)) {
+ if (prot >= MemoryProt::GpuRead) {
+ // If this mapping has GPU access, unmap from GPU.
rasterizer->UnmapMemory(virtual_addr, size);
}
@@ -528,6 +481,7 @@ 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;
@@ -536,8 +490,9 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
flexible_usage -= adjusted_size;
}
- if (IsValidGpuMapping(virtual_addr, adjusted_size)) {
- rasterizer->UnmapMemory(virtual_addr, 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.
@@ -605,8 +560,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;
+ // On PS4, protecting freed memory does nothing.
+ return adjusted_size;
}
// Validate protection flags
@@ -621,6 +576,18 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
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;
@@ -798,12 +765,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 +810,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;
From 8fffdc39186f1748460c637fab251271fbc257d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Thu, 29 May 2025 21:20:16 +0200
Subject: [PATCH 02/31] Handle V_CVT_F64_U32 (#3008)
---
src/shader_recompiler/frontend/translate/translate.h | 1 +
src/shader_recompiler/frontend/translate/vector_alu.cpp | 7 +++++++
2 files changed, 8 insertions(+)
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));
From 2091bc56513fd072ae8477b51c9a20be7817e618 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Fri, 30 May 2025 01:56:24 +0200
Subject: [PATCH 03/31] Handle R128 bit in MIMG instructions (#3010)
---
src/shader_recompiler/frontend/decode.cpp | 1 -
.../frontend/translate/vector_memory.cpp | 5 +++++
src/shader_recompiler/info.h | 9 ++++++++-
.../ir/passes/resource_tracking_pass.cpp | 1 +
src/shader_recompiler/ir/reg.h | 1 +
5 files changed, 15 insertions(+), 2 deletions(-)
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/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp
index 5639bc56a..8c1946390 100644
--- a/src/shader_recompiler/frontend/translate/vector_memory.cpp
+++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp
@@ -377,6 +377,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 +427,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 +453,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 +512,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 +621,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 {
From 790b54bf2953fd03b933c2e5d5247db41b76444a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Fri, 30 May 2025 03:51:36 +0200
Subject: [PATCH 04/31] Misc opcodes fixes (#3009)
---
src/shader_recompiler/frontend/translate/translate.cpp | 2 +-
src/shader_recompiler/frontend/translate/vector_memory.cpp | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
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/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp
index 8c1946390..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:
From 4019319d92b289e40fae8257e94562b949fca3e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Fri, 30 May 2025 21:04:31 +0200
Subject: [PATCH 05/31] Provide custom border color to samplers (#3014)
---
src/video_core/amdgpu/liverpool.h | 15 ++++++++-
.../renderer_vulkan/vk_rasterizer.cpp | 2 +-
src/video_core/texture_cache/sampler.cpp | 31 +++++++++++++++++--
src/video_core/texture_cache/sampler.h | 3 +-
.../texture_cache/texture_cache.cpp | 6 ++--
src/video_core/texture_cache/texture_cache.h | 4 ++-
6 files changed, 53 insertions(+), 8 deletions(-)
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) {
From c09e463b8ee86233ee883c1357bc883ce97bccaa Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Sat, 31 May 2025 09:35:52 -0500
Subject: [PATCH 06/31] Revert new GPU map logic (#3019)
Something's wrong somewhere, and there's just too many places that somewhere could be for me to debug it right now.
---
src/core/memory.cpp | 51 ++++++++++++++-------------------------------
1 file changed, 16 insertions(+), 35 deletions(-)
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ab59219b2..bf9d1cabd 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -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);
@@ -576,18 +569,6 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
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;
From bb199865cf2f09695535d98db38dc7c78fd2d054 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com>
Date: Sun, 1 Jun 2025 13:52:09 +0200
Subject: [PATCH 07/31] Qt: Set Minimum Icon Size List to 48 (#3018)
---
src/qt_gui/main_window.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 1966aa52b..77b088b78 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -387,9 +387,9 @@ void MainWindow::CreateConnects() {
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;
From b1af1334c91382d0e28b9cec06f4604d93357bea Mon Sep 17 00:00:00 2001
From: WujekFoliarz <72314465+WujekFoliarz@users.noreply.github.com>
Date: Sun, 1 Jun 2025 18:13:02 +0200
Subject: [PATCH 08/31] Fix touchpad handling and change gyro calculation
(#3006)
* Change touchpad handling and orientation calculation
* remove unnecessary includes in pad.cpp
* remove the cmake command arguments
* remove the weird file
* try to fix formatting
* limit new gyro and touchpad logic to controller 1
* remove cout
* fix formatting and add the handle check to scePadRead
* swap y and z back
---
src/core/libraries/pad/pad.cpp | 137 +++++++++++++++++++++++++++++---
src/input/controller.cpp | 141 ++++++++++++++++++++-------------
src/input/controller.h | 22 +++++
3 files changed, 232 insertions(+), 68 deletions(-)
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/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;
};
From 6bdd83684bc8f3a9bd7e960ae55eea669fc152dd Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Sun, 1 Jun 2025 11:30:03 -0500
Subject: [PATCH 09/31] Libs: libSceVoice stubs (#3022)
* voice lib stubs
Primarily for GTA V
* Clang
---
CMakeLists.txt | 2 +
src/common/logging/filter.cpp | 1 +
src/common/logging/types.h | 1 +
src/core/libraries/libs.cpp | 2 +
src/core/libraries/voice/voice.cpp | 203 +++++++++++++++++++++++++++++
src/core/libraries/voice/voice.h | 56 ++++++++
6 files changed, 265 insertions(+)
create mode 100644 src/core/libraries/voice/voice.cpp
create mode 100644 src/core/libraries/voice/voice.h
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/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/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/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
From 1930a2132c8c5b61eb72b0646f6712716c900b3f Mon Sep 17 00:00:00 2001
From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com>
Date: Mon, 2 Jun 2025 18:02:37 +0200
Subject: [PATCH 10/31] Fix SSH remote links (#3025)
---
src/common/scm_rev.cpp.in | 23 +++++++++++++++++++++++
src/common/scm_rev.h | 4 ++++
src/emulator.cpp | 10 +---------
src/qt_gui/main_window.cpp | 10 +---------
4 files changed, 29 insertions(+), 18 deletions(-)
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/emulator.cpp b/src/emulator.cpp
index 2ad8446ab..4fcb0cfc2 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -194,15 +194,7 @@ 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);
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 77b088b78..0f6773751 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);
From 2c782721851ba4bb66ad5964944c4f6ee6f990f7 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Tue, 3 Jun 2025 00:02:51 -0500
Subject: [PATCH 11/31] Emulate libSceGnmDriver's init behavior (#3024)
This time, perform it after the LoadSharedLibraries call, which places it after the various init memory mappings GFD engine titles perform.
---
src/core/linker.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
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;
From eed99141b3170b6417c9403df74bbb3e0920d915 Mon Sep 17 00:00:00 2001
From: mailwl
Date: Tue, 3 Jun 2025 09:11:18 +0300
Subject: [PATCH 12/31] Network Play: set user signed in (#2944)
* Network Play: set user signed in
* get signedIn status from config
---------
Co-authored-by: georgemoralis
---
src/common/config.cpp | 14 +++++++-
src/common/config.h | 4 ++-
src/core/libraries/np_manager/np_manager.cpp | 36 ++++++++++++++------
src/core/libraries/np_manager/np_manager.h | 9 ++++-
4 files changed, 49 insertions(+), 14 deletions(-)
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/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);
From bb3f8af81ad6b4c60cd8d1d33c18e7472759fe57 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Tue, 3 Jun 2025 01:29:25 -0500
Subject: [PATCH 13/31] Core: Protect fixes (#3029)
* Swap do-while to while
If we use a do-while loop, we waste time if `aligned_size = 0`. This is also still accurate to FreeBSD behavior, where it returns success if `start == end` during mprotect.
This also effectively prevents the memory assert seen in updated versions of RESIDENT EVIL 2 (CUSA09193)
* Move prot validation outside loop
The prot variable shouldn't change during a mprotect call, so we can check the flags before protecting instead.
Also cleans up the code for prot validation.
This should improve performance, and is more accurate to FreeBSD code.
* Add logging for protect calls
This will help in debugging future problems
---
src/core/libraries/kernel/memory.cpp | 4 ++++
src/core/memory.cpp | 32 +++++++++++++++-------------
2 files changed, 21 insertions(+), 15 deletions(-)
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/memory.cpp b/src/core/memory.cpp
index bf9d1cabd..8550ece17 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -557,18 +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;
- }
-
// Change protection
vma_base.prot = prot;
@@ -598,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",
@@ -615,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;
}
From ca0f458505e42bcab4b2c277b8abd7d302f760be Mon Sep 17 00:00:00 2001
From: Schweeeeeeeeeeeeeeee
<88291316+Schweeeeeeeeeeeeeeee@users.noreply.github.com>
Date: Tue, 3 Jun 2025 08:40:31 +0200
Subject: [PATCH 14/31] Add missing dependency for Fedora (#3005)
Propablly missing because of fedora 42?
---
documents/building-linux.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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
From c09d1c3cffa8591db056347c05063a9cd2b6c84e Mon Sep 17 00:00:00 2001
From: Fire Cube
Date: Tue, 3 Jun 2025 12:34:29 +0200
Subject: [PATCH 15/31] Add support for game folder and fail early if eboot.bin
is missing or corrupt (#3027)
---
src/emulator.cpp | 19 +++++++++++++++++--
src/emulator.h | 2 +-
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 4fcb0cfc2..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::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:
From 8cdd8dd725ae5c5b383ead35ad3eff59b3b55d09 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Tue, 3 Jun 2025 05:43:56 -0500
Subject: [PATCH 16/31] Core: Pthread affinity fixups (#3021)
* posix_pthread_attr_getschedparam
* Fixes for scePthreadGetAffinity and scePthreadSetAffinity
Looking at FreeBSD source, and our other pthread functions, we should be using our FindThread function to get the appropriate thread if thread != g_curthread.
---
src/core/libraries/kernel/threads/pthread.cpp | 31 ++++++++++++++++---
.../libraries/kernel/threads/pthread_attr.cpp | 2 ++
2 files changed, 29 insertions(+), 4 deletions(-)
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,
From 5b6fc788b31bfa3ed6e3f81770b2751f87d31fcc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Tue, 3 Jun 2025 20:42:20 +0200
Subject: [PATCH 17/31] Fix passing user data in user-triggered equeue events
(#2948)
---
src/core/libraries/kernel/equeue.cpp | 2 ++
src/core/libraries/kernel/equeue.h | 6 ++++++
2 files changed, 8 insertions(+)
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) {
From 88f6cb4d4197748c2dde637e3c9340823193228c Mon Sep 17 00:00:00 2001
From: Dmugetsu <168934208+diegolix29@users.noreply.github.com>
Date: Tue, 3 Jun 2025 15:26:04 -0600
Subject: [PATCH 18/31] clean up main window from extraction remains (#3035)
---
src/qt_gui/main_window.cpp | 1 -
src/qt_gui/main_window.h | 1 -
2 files changed, 2 deletions(-)
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 0f6773751..906a3066e 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -372,7 +372,6 @@ 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);
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);
From 23710f397eae17724777cc3dfa5333163c552f89 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Wed, 4 Jun 2025 00:17:35 -0500
Subject: [PATCH 19/31] Reduce clamp threshold to 2MB (#3034)
A proposed solution to the non-GPU memory asserts seen in RESIDENT EVIL 2 (CUSA09193)
---
src/core/memory.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 8550ece17..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;
From 4d1a1ce9c2aae933a3d4e9a0a07e55eb5e5875e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Thu, 5 Jun 2025 01:55:47 +0200
Subject: [PATCH 20/31] v_rcp_legacy_f32 (#3040)
---
.../frontend/translate/translate.h | 1 +
.../frontend/translate/vector_alu.cpp | 16 ++++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h
index 7b4b03f27..2584d5c5e 100644
--- a/src/shader_recompiler/frontend/translate/translate.h
+++ b/src/shader_recompiler/frontend/translate/translate.h
@@ -204,6 +204,7 @@ public:
void V_EXP_F32(const GcnInst& inst);
void V_LOG_F32(const GcnInst& inst);
void V_RCP_F32(const GcnInst& inst);
+ void V_RCP_LEGACY_F32(const GcnInst& inst);
void V_RCP_F64(const GcnInst& inst);
void V_RSQ_F32(const GcnInst& inst);
void V_SQRT_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 fb3f52c7f..3b88e4dec 100644
--- a/src/shader_recompiler/frontend/translate/vector_alu.cpp
+++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp
@@ -158,6 +158,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
return V_LOG_F32(inst);
case Opcode::V_RCP_F32:
return V_RCP_F32(inst);
+ case Opcode::V_RCP_LEGACY_F32:
+ return V_RCP_LEGACY_F32(inst);
case Opcode::V_RCP_F64:
return V_RCP_F64(inst);
case Opcode::V_RCP_IFLAG_F32:
@@ -798,6 +800,20 @@ void Translator::V_RCP_F32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.FPRecip(src0));
}
+void Translator::V_RCP_LEGACY_F32(const GcnInst& inst) {
+ const IR::F32 src0{GetSrc(inst.src[0])};
+ const auto result = ir.FPRecip(src0);
+ const auto inf = ir.FPIsInf(result);
+
+ const auto raw_result = ir.ConvertFToU(32, result);
+ const auto sign_bit = ir.ShiftRightLogical(raw_result, ir.Imm32(31u));
+ const auto sign_bit_set = ir.INotEqual(sign_bit, ir.Imm32(0u));
+ const IR::F32 inf_result{ir.Select(sign_bit_set, ir.Imm32(-0.0f), ir.Imm32(0.0f))};
+ const IR::F32 val{ir.Select(inf, inf_result, result)};
+
+ SetDst(inst.dst[0], val);
+}
+
void Translator::V_RCP_F64(const GcnInst& inst) {
const IR::F64 src0{GetSrc64(inst.src[0])};
SetDst64(inst.dst[0], ir.FPRecip(src0));
From d4fbeea085d704a94151e44a5dcf686e2d33a7b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Thu, 5 Jun 2025 02:00:11 +0200
Subject: [PATCH 21/31] Stub PM4 COPY_DATA opcode (#3032)
---
src/video_core/amdgpu/liverpool.cpp | 15 ++++++--
src/video_core/amdgpu/pm4_cmds.h | 55 +++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp
index 4db7648c6..118c43cef 100644
--- a/src/video_core/amdgpu/liverpool.cpp
+++ b/src/video_core/amdgpu/liverpool.cpp
@@ -394,7 +394,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span dcb, std::span(header);
- LOG_DEBUG(Render_Vulkan,
- "Encountered EventWrite: event_type = {}, event_index = {}",
+ LOG_DEBUG(Render, "Encountered EventWrite: event_type = {}, event_index = {}",
magic_enum::enum_name(event->event_type.Value()),
magic_enum::enum_name(event->event_index.Value()));
if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) {
@@ -673,6 +672,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header);
+ LOG_WARNING(Render,
+ "unhandled IT_COPY_DATA src_sel = {}, dst_sel = {}, "
+ "count_sel = {}, wr_confirm = {}, engine_sel = {}",
+ u32(copy_data->src_sel.Value()), u32(copy_data->dst_sel.Value()),
+ copy_data->count_sel.Value(), copy_data->wr_confirm.Value(),
+ u32(copy_data->engine_sel.Value()));
+ break;
+ }
case PM4ItOpcode::MemSemaphore: {
const auto* mem_semaphore = reinterpret_cast(header);
if (mem_semaphore->IsSignaling()) {
diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h
index 58ecda93e..011e47bf0 100644
--- a/src/video_core/amdgpu/pm4_cmds.h
+++ b/src/video_core/amdgpu/pm4_cmds.h
@@ -554,6 +554,61 @@ struct PM4DmaData {
}
};
+enum class CopyDataSrc : u32 {
+ MappedRegister = 0,
+ Memory = 1,
+ TCL2 = 2,
+ Gds = 3,
+ // Reserved = 4,
+ Immediate = 5,
+ Atomic = 6,
+ GdsAtomic0 = 7,
+ GdsAtomic1 = 8,
+ GpuClock = 9,
+};
+
+enum class CopyDataDst : u32 {
+ MappedRegister = 0,
+ MemorySync = 1,
+ TCL2 = 2,
+ Gds = 3,
+ // Reserved = 4,
+ MemoryAsync = 5,
+};
+
+enum class CopyDataEngine : u32 {
+ Me = 0,
+ Pfp = 1,
+ Ce = 2,
+ // Reserved = 3
+};
+
+struct PM4CmdCopyData {
+ PM4Type3Header header;
+ union {
+ BitField<0, 4, CopyDataSrc> src_sel;
+ BitField<8, 4, CopyDataDst> dst_sel;
+ BitField<16, 1, u32> count_sel;
+ BitField<20, 1, u32> wr_confirm;
+ BitField<30, 2, CopyDataEngine> engine_sel;
+ u32 control;
+ };
+ u32 src_addr_lo;
+ u32 src_addr_hi;
+ u32 dst_addr_lo;
+ u32 dst_addr_hi;
+
+ template
+ T SrcAddress() const {
+ return std::bit_cast(src_addr_lo | u64(src_addr_hi) << 32);
+ }
+
+ template
+ T DstAddress() const {
+ return std::bit_cast(dst_addr_lo | u64(dst_addr_hi) << 32);
+ }
+};
+
struct PM4CmdRewind {
PM4Type3Header header;
union {
From 285df1b5befcedb1287007ed992e1805b148025f Mon Sep 17 00:00:00 2001
From: DanielSvoboda
Date: Thu, 5 Jun 2025 02:48:47 -0300
Subject: [PATCH 22/31] QT: AutoUpdate - Fix Changelog Error (#3042)
---
.github/workflows/build.yml | 6 +++---
src/qt_gui/check_update.cpp | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ceb915f6a..bb3d157b7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -494,7 +494,7 @@ jobs:
with:
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
- tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
+ tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}"
draft: false
prerelease: true
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
@@ -530,14 +530,14 @@ jobs:
# Check if release already exists and get ID
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
- "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
+ "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}" | jq -r '.id')
if [[ "$release_id" == "null" ]]; then
echo "Creating release in $REPO for $filename"
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
- "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
+ "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}",
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"draft": false,
"prerelease": true,
diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp
index 550fdddb5..b0858840a 100644
--- a/src/qt_gui/check_update.cpp
+++ b/src/qt_gui/check_update.cpp
@@ -137,7 +137,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
}
}
- latestRev = latestVersion.right(7);
+ latestRev = latestVersion.right(40);
latestDate = jsonObj["published_at"].toString();
QJsonArray assets = jsonObj["assets"].toArray();
@@ -167,7 +167,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
- if (latestRev == currentRev.left(7)) {
+ if (latestRev == currentRev) {
if (showMessage) {
QMessageBox::information(this, tr("Auto Updater"),
tr("Your version is already up to date!"));
@@ -215,7 +215,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
"%3 | "
"(%4) | "
"
")
- .arg(currentRev.left(7), currentDate, latestRev, latestDate);
+ .arg(currentRev.left(7), currentDate, latestRev.left(7), latestDate);
QLabel* updateLabel = new QLabel(updateText, this);
layout->addWidget(updateLabel);
From 93222c6f9f01c15855b3cee23c7856b963b6b1e2 Mon Sep 17 00:00:00 2001
From: georgemoralis
Date: Thu, 5 Jun 2025 08:49:32 +0300
Subject: [PATCH 23/31] New Crowdin updates (#3038)
* New translations en_us.ts (Portuguese, Brazilian)
* New translations en_us.ts (Turkish)
---
src/qt_gui/translations/pt_BR.ts | 2 +-
src/qt_gui/translations/tr_TR.ts | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts
index 34d31f240..9f254e272 100644
--- a/src/qt_gui/translations/pt_BR.ts
+++ b/src/qt_gui/translations/pt_BR.ts
@@ -2048,7 +2048,7 @@
* Unsupported Vulkan Version
- * Unsupported Vulkan Version
+ * Versão do Vulkan não suportada
diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts
index e61985e90..c6d641470 100644
--- a/src/qt_gui/translations/tr_TR.ts
+++ b/src/qt_gui/translations/tr_TR.ts
@@ -138,7 +138,7 @@
File Exists
- Dosya mevcut
+ Dosya Mevcut
File already exists. Do you want to replace it?
@@ -1221,7 +1221,7 @@
Exit shadPS4
- shadPS4'ten Çık
+ shadPS4 Çıkış
Exit the application.
@@ -1381,7 +1381,7 @@
Game Boot
- Oyun Başlatma
+ Oyun Başlat
Only one file can be selected!
From 0e9420a7b228f3e560ba154ad33e037358679638 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Thu, 5 Jun 2025 08:43:39 -0500
Subject: [PATCH 24/31] Fix request queues in libSceZlib (#3041)
Queues are a FIFO data structure, so pop() removes the front, not the end.
---
src/core/libraries/zlib/zlib.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/core/libraries/zlib/zlib.cpp b/src/core/libraries/zlib/zlib.cpp
index 899cb5bf6..b304992ad 100644
--- a/src/core/libraries/zlib/zlib.cpp
+++ b/src/core/libraries/zlib/zlib.cpp
@@ -51,7 +51,7 @@ void ZlibTaskThread(const std::stop_token& stop) {
if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) {
break;
}
- task = task_queue.back();
+ task = task_queue.front();
task_queue.pop();
}
@@ -136,7 +136,7 @@ s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) {
} else {
done_queue_cv.wait(lock, pred);
}
- *request_id = done_queue.back();
+ *request_id = done_queue.front();
done_queue.pop();
}
return ORBIS_OK;
From 3b3026ff1c98137e4f3051ec44c0eb4e1fa2f8ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com>
Date: Thu, 5 Jun 2025 15:44:02 +0200
Subject: [PATCH 25/31] [CI] Update Qt to 6.9.1 (#3037)
---
.github/workflows/build.yml | 30 ++++++++++--------------------
1 file changed, 10 insertions(+), 20 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bb3d157b7..588236b14 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -76,18 +76,13 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- - name: Setup VS Environment
- uses: ilammy/msvc-dev-cmd@v1.13.0
- with:
- arch: amd64
-
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@@ -111,7 +106,7 @@ jobs:
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.9.0
+ version: 6.9.1
host: windows
target: desktop
arch: win64_msvc2022_64
@@ -130,18 +125,13 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- - name: Setup VS Environment
- uses: ilammy/msvc-dev-cmd@v1.13.0
- with:
- arch: amd64
-
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@@ -186,7 +176,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{runner.os}}-sdl-cache-cmake-build
with:
@@ -228,7 +218,7 @@ jobs:
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.9.0
+ version: 6.9.1
host: mac
target: desktop
arch: clang_64
@@ -247,7 +237,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{runner.os}}-qt-cache-cmake-build
with:
@@ -301,7 +291,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
@@ -362,7 +352,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
@@ -409,7 +399,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
with:
@@ -445,7 +435,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
with:
From 43bf4ed1bca1cd6442e97b04d9afb8383219bb86 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Thu, 5 Jun 2025 14:14:34 -0500
Subject: [PATCH 26/31] sceVideoOutGetResolutionStatus error behavior (#3044)
---
src/core/libraries/videoout/video_out.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp
index c5208b6dd..da715b3bf 100644
--- a/src/core/libraries/videoout/video_out.cpp
+++ b/src/core/libraries/videoout/video_out.cpp
@@ -282,7 +282,12 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
LOG_INFO(Lib_VideoOut, "called");
- *status = driver->GetPort(handle)->resolution;
+ auto* port = driver->GetPort(handle);
+ if (!port || !port->is_open) {
+ return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
+ }
+
+ *status = port->resolution;
return ORBIS_OK;
}
From fff3bf9917faef7a185cae896efce87bea7b5b50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Thu, 5 Jun 2025 23:33:25 +0200
Subject: [PATCH 27/31] s_flbit_i32_b64 (#3033)
* s_flbit_i32_b64
* Split FindUMsb64 into two 32bit ops
---
.../backend/spirv/emit_spirv_instructions.h | 1 +
.../backend/spirv/emit_spirv_integer.cpp | 14 ++++++++++++++
.../frontend/translate/scalar_alu.cpp | 13 +++++++++++++
.../frontend/translate/translate.h | 1 +
src/shader_recompiler/ir/ir_emitter.cpp | 11 +++++++++--
src/shader_recompiler/ir/ir_emitter.h | 2 +-
src/shader_recompiler/ir/opcodes.inc | 1 +
7 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 09f9732bf..172358866 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -372,6 +372,7 @@ Id EmitBitCount64(EmitContext& ctx, Id value);
Id EmitBitwiseNot32(EmitContext& ctx, Id value);
Id EmitFindSMsb32(EmitContext& ctx, Id value);
Id EmitFindUMsb32(EmitContext& ctx, Id value);
+Id EmitFindUMsb64(EmitContext& ctx, Id value);
Id EmitFindILsb32(EmitContext& ctx, Id value);
Id EmitFindILsb64(EmitContext& ctx, Id value);
Id EmitSMin32(EmitContext& ctx, Id a, Id b);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
index 10bfbb2ab..1a995354d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -229,6 +229,20 @@ Id EmitFindUMsb32(EmitContext& ctx, Id value) {
return ctx.OpFindUMsb(ctx.U32[1], value);
}
+Id EmitFindUMsb64(EmitContext& ctx, Id value) {
+ // Vulkan restricts some bitwise operations to 32-bit only, so decompose into
+ // two 32-bit values and select the correct result.
+ const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)};
+ const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)};
+ const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)};
+ const Id hi_msb{ctx.OpFindUMsb(ctx.U32[1], hi)};
+ const Id lo_msb{ctx.OpFindUMsb(ctx.U32[1], lo)};
+ const Id found_hi{ctx.OpINotEqual(ctx.U1[1], hi_msb, ctx.ConstU32(u32(-1)))};
+ const Id shifted_hi{ctx.OpIAdd(ctx.U32[1], hi_msb, ctx.ConstU32(32u))};
+ // value == 0 case is checked in IREmitter
+ return ctx.OpSelect(ctx.U32[1], found_hi, shifted_hi, lo_msb);
+}
+
Id EmitFindILsb32(EmitContext& ctx, Id value) {
return ctx.OpFindILsb(ctx.U32[1], value);
}
diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp
index 3a8e894ae..7beb594c3 100644
--- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp
+++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp
@@ -114,6 +114,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) {
return S_FF1_I32_B64(inst);
case Opcode::S_FLBIT_I32_B32:
return S_FLBIT_I32_B32(inst);
+ case Opcode::S_FLBIT_I32_B64:
+ return S_FLBIT_I32_B64(inst);
case Opcode::S_BITSET0_B32:
return S_BITSET_B32(inst, 0);
case Opcode::S_BITSET1_B32:
@@ -686,6 +688,17 @@ void Translator::S_FLBIT_I32_B32(const GcnInst& inst) {
SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))});
}
+void Translator::S_FLBIT_I32_B64(const GcnInst& inst) {
+ const IR::U64 src0{GetSrc64(inst.src[0])};
+ // Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB)
+ // position
+ const IR::U32 msb_pos = ir.FindUMsb(src0);
+ const IR::U32 pos_from_left = ir.ISub(ir.Imm32(63), msb_pos);
+ // Select 0xFFFFFFFF if src0 was 0
+ const IR::U1 cond = ir.INotEqual(src0, ir.Imm64(u64(0u)));
+ SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))});
+}
+
void Translator::S_BITSET_B32(const GcnInst& inst, u32 bit_value) {
const IR::U32 old_value{GetSrc(inst.dst[0])};
const IR::U32 offset{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0U), ir.Imm32(5U))};
diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h
index 2584d5c5e..15ba8c8d7 100644
--- a/src/shader_recompiler/frontend/translate/translate.h
+++ b/src/shader_recompiler/frontend/translate/translate.h
@@ -121,6 +121,7 @@ public:
void S_FF1_I32_B32(const GcnInst& inst);
void S_FF1_I32_B64(const GcnInst& inst);
void S_FLBIT_I32_B32(const GcnInst& inst);
+ void S_FLBIT_I32_B64(const GcnInst& inst);
void S_BITSET_B32(const GcnInst& inst, u32 bit_value);
void S_GETPC_B64(u32 pc, const GcnInst& inst);
void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst);
diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp
index 01d945178..dcb734d01 100644
--- a/src/shader_recompiler/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/ir/ir_emitter.cpp
@@ -1546,8 +1546,15 @@ U32 IREmitter::FindSMsb(const U32& value) {
return Inst(Opcode::FindSMsb32, value);
}
-U32 IREmitter::FindUMsb(const U32& value) {
- return Inst(Opcode::FindUMsb32, value);
+U32 IREmitter::FindUMsb(const U32U64& value) {
+ switch (value.Type()) {
+ case Type::U32:
+ return Inst(Opcode::FindUMsb32, value);
+ case Type::U64:
+ return Inst(Opcode::FindUMsb64, value);
+ default:
+ ThrowInvalidType(value.Type());
+ }
}
U32 IREmitter::FindILsb(const U32U64& value) {
diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h
index 8f8a12736..da7adf42b 100644
--- a/src/shader_recompiler/ir/ir_emitter.h
+++ b/src/shader_recompiler/ir/ir_emitter.h
@@ -266,7 +266,7 @@ public:
[[nodiscard]] U32 BitwiseNot(const U32& value);
[[nodiscard]] U32 FindSMsb(const U32& value);
- [[nodiscard]] U32 FindUMsb(const U32& value);
+ [[nodiscard]] U32 FindUMsb(const U32U64& value);
[[nodiscard]] U32 FindILsb(const U32U64& value);
[[nodiscard]] U32 SMin(const U32& a, const U32& b);
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc
index ab6dbfde9..647432bcf 100644
--- a/src/shader_recompiler/ir/opcodes.inc
+++ b/src/shader_recompiler/ir/opcodes.inc
@@ -349,6 +349,7 @@ OPCODE(BitwiseNot32, U32, U32,
OPCODE(FindSMsb32, U32, U32, )
OPCODE(FindUMsb32, U32, U32, )
+OPCODE(FindUMsb64, U32, U64, )
OPCODE(FindILsb32, U32, U32, )
OPCODE(FindILsb64, U32, U64, )
OPCODE(SMin32, U32, U32, U32, )
From 91d29459fb55cb0d28006639e7a38134c5a368ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Fri, 6 Jun 2025 05:19:05 +0200
Subject: [PATCH 28/31] Implement PM4CondExec (#3046)
---
src/video_core/amdgpu/liverpool.cpp | 13 +++++++++++++
src/video_core/amdgpu/pm4_cmds.h | 21 +++++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp
index 118c43cef..e031d0ebc 100644
--- a/src/video_core/amdgpu/liverpool.cpp
+++ b/src/video_core/amdgpu/liverpool.cpp
@@ -765,6 +765,19 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header);
+ if (cond_exec->command.Value() != 0) {
+ LOG_WARNING(Render, "IT_COND_EXEC used a reserved command");
+ }
+ const auto skip = *cond_exec->Address() == false;
+ if (skip) {
+ dcb = NextPacket(dcb,
+ header->type3.NumWords() + 1 + cond_exec->exec_count.Value());
+ continue;
+ }
+ break;
+ }
default:
UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}",
static_cast(opcode), count);
diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h
index 011e47bf0..23c1b8f21 100644
--- a/src/video_core/amdgpu/pm4_cmds.h
+++ b/src/video_core/amdgpu/pm4_cmds.h
@@ -1159,4 +1159,25 @@ struct PM4CmdMemSemaphore {
}
};
+struct PM4CmdCondExec {
+ PM4Type3Header header;
+ union {
+ BitField<2, 30, u32> bool_addr_lo; ///< low 32 address bits for the block in memory from
+ ///< where the CP will fetch the condition
+ };
+ union {
+ BitField<0, 16, u32> bool_addr_hi; ///< high address bits for the condition
+ BitField<28, 4, u32> command;
+ };
+ union {
+ BitField<0, 14, u32> exec_count; ///< Number of DWords that the CP will skip
+ ///< if bool pointed to is zero
+ };
+
+ bool* Address() const {
+ return std::bit_cast(u64(bool_addr_hi.Value()) << 32 | u64(bool_addr_lo.Value())
+ << 2);
+ }
+};
+
} // namespace AmdGpu
From 5edd9ff54b6baca5025dff0f5491ddf0d0746a19 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Sat, 7 Jun 2025 16:17:45 -0500
Subject: [PATCH 29/31] Improved sceKernelMapNamedFlexibleMemory logging
(#3050)
* More descriptive sceKernelMapNamedFlexibleMemory logging
* Misc exports
These functions are used by Overwatch: Origins Edition
* Clang
* Function parameter cleanup
Changes the parameters on our sceKernelMapNamedFlexibleMemory and sceKernelMapFlexibleMemory functions to better align with our current standards.
---
src/core/libraries/kernel/kernel.cpp | 4 ++++
src/core/libraries/kernel/memory.cpp | 21 +++++++++------------
src/core/libraries/kernel/memory.h | 7 +++----
src/core/libraries/kernel/threads/mutex.cpp | 1 +
4 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp
index 180850217..930640d0e 100644
--- a/src/core/libraries/kernel/kernel.cpp
+++ b/src/core/libraries/kernel/kernel.cpp
@@ -273,6 +273,10 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...
LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ...
+ LIB_FUNCTION("XVL8So3QJUk", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_connect);
+ LIB_FUNCTION("3e+4Iv7IJ8U", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
+ LIB_FUNCTION("aNeavPDNKzA", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendmsg);
+ LIB_FUNCTION("pxnCmagrtao", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
}
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index 18676cbdf..5e94199e1 100644
--- a/src/core/libraries/kernel/memory.cpp
+++ b/src/core/libraries/kernel/memory.cpp
@@ -222,9 +222,10 @@ s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 p
return ret;
}
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags, const char* name) {
-
+s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
+ const char* name) {
+ LOG_INFO(Kernel_Vmm, "in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, name = '{}'",
+ fmt::ptr(*addr_in_out), len, prot, flags, name);
if (len == 0 || !Common::Is16KBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -243,18 +244,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
const VAddr in_addr = reinterpret_cast(*addr_in_out);
const auto mem_prot = static_cast(prot);
const auto map_flags = static_cast(flags);
- SCOPE_EXIT {
- LOG_INFO(Kernel_Vmm,
- "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}",
- in_addr, fmt::ptr(*addr_in_out), len, prot, flags);
- };
auto* memory = Core::Memory::Instance();
- return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
- Core::VMAType::Flexible, name);
+ const auto ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
+ Core::VMAType::Flexible, name);
+ LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr_in_out));
+ return ret;
}
-s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags) {
+s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags) {
return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon");
}
diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h
index 6cefe0d07..ea42e7546 100644
--- a/src/core/libraries/kernel/memory.h
+++ b/src/core/libraries/kernel/memory.h
@@ -141,10 +141,9 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
size_t infoSize);
s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment);
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot,
- int flags, const char* name);
-s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags);
+s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
+ const char* name);
+s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags);
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot);
s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot);
diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp
index 956e5ef65..3dbade96a 100644
--- a/src/core/libraries/kernel/threads/mutex.cpp
+++ b/src/core/libraries/kernel/threads/mutex.cpp
@@ -426,6 +426,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
// Posix
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init);
LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
+ LIB_FUNCTION("Io9+nTKXZtA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_timedlock);
LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
From 2857ef34f037ac8e96730faabdf7dd3511c7d6af Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Sun, 8 Jun 2025 13:04:43 -0500
Subject: [PATCH 30/31] Don't coalesce dmem pages (#3059)
Looks like this change is what broke P.T.
I'll need to look closer at this when I have a chance, clearly we're doing something wrong here.
---
src/core/memory.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ba3640877..54cae910b 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -182,7 +182,6 @@ 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;
}
From 5d064dd89f17b73be3cae84bd0bf98a7b02d6997 Mon Sep 17 00:00:00 2001
From: Fire Cube
Date: Sun, 8 Jun 2025 20:04:55 +0200
Subject: [PATCH 31/31] Dev Tools: Fix Module Viewer HLE detection (#3058)
* fix
* clang
---
src/core/linker.cpp | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/src/core/linker.cpp b/src/core/linker.cpp
index c50b03a8f..1f45caf12 100644
--- a/src/core/linker.cpp
+++ b/src/core/linker.cpp
@@ -332,21 +332,22 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
sr.type = sym_type;
const auto* record = m_hle_symbols.FindSymbol(sr);
- if (!record) {
- // Check if it an export function
- const auto* p = FindExportedModule(*module, *library);
- if (p && p->export_sym.GetSize() > 0) {
- record = p->export_sym.FindSymbol(sr);
- }
- }
if (record) {
*return_info = *record;
-
Core::Devtools::Widget::ModuleList::AddModule(sr.library);
-
return true;
}
+ // Check if it an export function
+ const auto* p = FindExportedModule(*module, *library);
+ if (p && p->export_sym.GetSize() > 0) {
+ record = p->export_sym.FindSymbol(sr);
+ if (record) {
+ *return_info = *record;
+ return true;
+ }
+ }
+
const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
if (aeronid) {
return_info->name = aeronid->name;