Merge branch 'main' into fontlib

This commit is contained in:
georgemoralis 2025-06-04 12:45:23 +03:00 committed by GitHub
commit 51ba366ad5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 925 additions and 326 deletions

View file

@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
set(AUDIO_LIB src/core/libraries/audio/audioin.cpp set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h 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.cpp
src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout.h
src/core/libraries/audio/audioout_backend.h src/core/libraries/audio/audioout_backend.h

View file

@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \ openssl-devel libevdev-devel libudev-devel libXext-devel \
qt6-qtbase-devel qt6-qtbase-private-devel \ qt6-qtbase-devel qt6-qtbase-private-devel \
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-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 #### Arch Linux

View file

@ -69,7 +69,7 @@ static bool vkGuestMarkers = false;
static bool rdocEnable = false; static bool rdocEnable = false;
static bool isFpsColor = true; static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false; static bool isSeparateLogFilesEnabled = false;
static s16 cursorState = HideCursorState::Idle; static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default) static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0; static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true; static bool useUnifiedInputConfig = true;
@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false; static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false; static bool checkCompatibilityOnStartup = false;
static std::string trophyKey; static std::string trophyKey;
static bool isPSNSignedIn = false;
// Gui // Gui
static bool load_game_size = true; static bool load_game_size = true;
@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) {
showBackgroundImage = show; showBackgroundImage = show;
} }
bool getPSNSignedIn() {
return isPSNSignedIn;
}
void setPSNSignedIn(bool sign) {
isPSNSignedIn = sign;
}
void load(const std::filesystem::path& path) { void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return // If the configuration file does not exist, create it and return
std::error_code error; std::error_code error;
@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<bool>(general, "isPS4Pro", false); isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isDevKit = toml::find_or<bool>(general, "isDevKit", false); isDevKit = toml::find_or<bool>(general, "isDevKit", false);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false);
playBGM = toml::find_or<bool>(general, "playBGM", false); playBGM = toml::find_or<bool>(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false); isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration = trophyNotificationDuration =
@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo; data["General"]["isPS4Pro"] = isNeo;
data["General"]["isDevKit"] = isDevKit; data["General"]["isDevKit"] = isDevKit;
data["General"]["isPSNSignedIn"] = isPSNSignedIn;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
data["General"]["playBGM"] = playBGM; data["General"]["playBGM"] = playBGM;
@ -1098,6 +1109,7 @@ void setDefaultValues() {
isHDRAllowed = false; isHDRAllowed = false;
isNeo = false; isNeo = false;
isDevKit = false; isDevKit = false;
isPSNSignedIn = false;
isFullscreen = false; isFullscreen = false;
isTrophyPopupDisabled = false; isTrophyPopupDisabled = false;
playBGM = false; playBGM = false;

View file

@ -14,7 +14,7 @@ struct GameInstallDir {
bool enabled; bool enabled;
}; };
enum HideCursorState : s16 { Never, Idle, Always }; enum HideCursorState : int { Never, Idle, Always };
void load(const std::filesystem::path& path); void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path); void save(const std::filesystem::path& path);
@ -39,6 +39,7 @@ bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup(); bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity(); int getBackgroundImageOpacity();
bool getShowBackgroundImage(); bool getShowBackgroundImage();
bool getPSNSignedIn();
std::string getLogFilter(); std::string getLogFilter();
std::string getLogType(); std::string getLogType();
@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use); void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity); void setBackgroundImageOpacity(int opacity);
void setShowBackgroundImage(bool show); void setShowBackgroundImage(bool show);
void setPSNSignedIn(bool sign);
void setCursorState(s16 cursorState); void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout); void setCursorHideTimeout(int newcursorHideTimeout);

View file

@ -143,6 +143,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Camera) \ SUB(Lib, Camera) \
SUB(Lib, CompanionHttpd) \ SUB(Lib, CompanionHttpd) \
SUB(Lib, CompanionUtil) \ SUB(Lib, CompanionUtil) \
SUB(Lib, Voice) \
CLS(Frontend) \ CLS(Frontend) \
CLS(Render) \ CLS(Render) \
SUB(Render, Vulkan) \ SUB(Render, Vulkan) \

View file

@ -98,6 +98,7 @@ enum class Class : u8 {
Lib_Fiber, ///< The LibSceFiber implementation. Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation. Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation

View file

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include "common/scm_rev.h" #include "common/scm_rev.h"
namespace Common { 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_remote_url[] = "@GIT_REMOTE_URL@";
constexpr char g_scm_date[] = "@BUILD_DATE@"; 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 } // namespace

View file

@ -3,6 +3,8 @@
#pragma once #pragma once
#include <string>
namespace Common { namespace Common {
extern const char g_version[]; 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_remote_url[];
extern const char g_scm_date[]; extern const char g_scm_date[];
const std::string GetRemoteNameFromLink();
} // namespace Common } // namespace Common

View file

@ -145,6 +145,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
if (event.event.ident == ident && event.event.filter == filter) { if (event.event.ident == ident && event.event.filter == filter) {
if (filter == SceKernelEvent::Filter::VideoOut) { if (filter == SceKernelEvent::Filter::VideoOut) {
event.TriggerDisplay(trigger_data); event.TriggerDisplay(trigger_data);
} else if (filter == SceKernelEvent::Filter::User) {
event.TriggerUser(trigger_data);
} else { } else {
event.Trigger(trigger_data); event.Trigger(trigger_data);
} }

View file

@ -98,6 +98,12 @@ struct EqueueEvent {
event.data = reinterpret_cast<uintptr_t>(data); event.data = reinterpret_cast<uintptr_t>(data);
} }
void TriggerUser(void* data) {
is_triggered = true;
event.fflags++;
event.udata = data;
}
void TriggerDisplay(void* data) { void TriggerDisplay(void* data) {
is_triggered = true; is_triggered = true;
if (data != nullptr) { if (data != nullptr) {

View file

@ -8,7 +8,6 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.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<VAddr>(*addr); const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(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) { if (result == 0) {
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
} }
@ -263,13 +263,26 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
return memory->QueryProtection(std::bit_cast<VAddr>(addr), start, end, prot); return memory->QueryProtection(std::bit_cast<VAddr>(addr), start, end, prot);
} }
int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) { s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot); Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags); return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
} }
int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) { s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
s32 result = sceKernelMprotect(addr, size, prot);
if (result < 0) {
ErrSceToPosix(result);
return -1;
}
return result;
}
s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) {
LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot); Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags); return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
@ -344,7 +357,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break; break;
} }
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: { 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, LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result); entries[i].operation, entries[i].length, result);
break; break;
@ -359,7 +372,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break; break;
} }
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: { 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); entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result); entries[i].operation, entries[i].length, result);
@ -380,7 +393,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
return result; 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) { if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!"); LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT; return ORBIS_KERNEL_ERROR_EFAULT;
@ -396,8 +409,8 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
size_t alignment, u64* physAddrOut) { u64* physAddrOut) {
if (searchStart < 0 || searchEnd <= searchStart) { if (searchStart < 0 || searchEnd <= searchStart) {
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -439,10 +452,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
void** addrOut) { void** addr_out) {
LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
fmt::ptr(addrIn), len, alignment, flags); fmt::ptr(addr_in), len, alignment, flags);
if (len == 0 || !Common::Is2MBAligned(len)) { if (len == 0 || !Common::Is2MBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!");
@ -456,14 +469,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali
} }
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
const VAddr in_addr = reinterpret_cast<VAddr>(addrIn); const VAddr in_addr = reinterpret_cast<VAddr>(addr_in);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(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<VAddr>(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) { if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!"); LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -482,7 +497,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int
return memory->PoolCommit(in_addr, len, mem_prot); 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) { if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!"); LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -523,12 +538,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
break; break;
} }
case OrbisKernelMemoryPoolOpcode::Protect: { 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); entry.protect_params.prot);
break; break;
} }
case OrbisKernelMemoryPoolOpcode::TypeProtect: { case OrbisKernelMemoryPoolOpcode::TypeProtect: {
result = sceKernelMTypeProtect( result = sceKernelMtypeprotect(
entry.type_protect_params.addr, entry.type_protect_params.len, entry.type_protect_params.addr, entry.type_protect_params.len,
entry.type_protect_params.type, entry.type_protect_params.prot); entry.type_protect_params.type, entry.type_protect_params.prot);
break; break;
@ -553,30 +568,48 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
return result; return result;
} }
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) {
void** res) { LOG_INFO(Kernel_Vmm,
LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}",
fmt::ptr(addr), len, prot, flags, fd, offset); fmt::ptr(addr), len, prot, flags, fd, phys_addr);
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
void* addr_out;
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
const auto mem_prot = static_cast<Core::MemoryProt>(prot); const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags); const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
s32 result = ORBIS_OK;
if (fd == -1) { if (fd == -1) {
return memory->MapMemory(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, result = memory->MapMemory(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
Core::VMAType::Flexible); Core::VMAType::Flexible);
} else { } else {
const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); result = memory->MapFile(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
return memory->MapFile(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, handle, fd, phys_addr);
offset);
} }
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<void*>(-1);
}
return addr_out;
} }
void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr,
void* ptr; void** res) {
LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr);
int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
ASSERT(result == 0); if (addr_out == reinterpret_cast<void*>(-1)) {
return ptr; // 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) { s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
@ -678,8 +711,9 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
sceKernelConfiguredFlexibleMemorySize); 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 // Memory pool
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);

View file

@ -147,9 +147,9 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len,
int flags); int flags);
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot); 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, int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize); 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, s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut, int flags); 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, s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
size_t alignment, u64* physAddrOut); u64* physAddrOut);
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
void** addrOut); void** addr_out);
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);
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags);
s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count,
s32* num_processed, s32 flags); s32* num_processed, s32 flags);

View file

@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) { if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL; 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; 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, 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) { if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL; 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 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) { int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {

View file

@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
posix_pthread_attr_getdetachstate); posix_pthread_attr_getdetachstate);
LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1, LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setguardsize); posix_pthread_attr_setguardsize);
LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_getschedparam);
// Orbis // Orbis
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,

View file

@ -60,6 +60,7 @@
#include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h" #include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.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/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h" #include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h" #include "fiber/fiber.h"
@ -128,6 +129,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Camera::RegisterlibSceCamera(sym); Libraries::Camera::RegisterlibSceCamera(sym);
Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym); Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym); Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym);
Libraries::Voice::RegisterlibSceVoice(sym);
} }
} // namespace Libraries } // namespace Libraries

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/config.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -10,6 +11,8 @@
namespace Libraries::NpManager { namespace Libraries::NpManager {
#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT)
int PS4_SYSV_ABI Func_EF4378573542A508() { int PS4_SYSV_ABI Func_EF4378573542A508() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called"); LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNpGetAccountCountryA() { int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
LOG_ERROR(Lib_NpManager, "(STUBBED) called"); OrbisNpCountryCode* country_code) {
return ORBIS_OK; 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() { 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) { if (online_id == nullptr || account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
*account_id = 0; *account_id = 0xFEEDFACE;
return ORBIS_NP_ERROR_SIGNED_OUT; return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { 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) { if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
*account_id = 0; *account_id = 0xFEEDFACE;
return ORBIS_NP_ERROR_SIGNED_OUT; return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetAccountLanguage() { int PS4_SYSV_ABI sceNpGetAccountLanguage() {
@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id)
if (np_id == nullptr) { if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; 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() { int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI
if (online_id == nullptr) { if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; 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() { int PS4_SYSV_ABI sceNpGetParentalControlInfo() {
@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta
if (state == nullptr) { if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
*state = OrbisNpState::SignedOut; *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
LOG_DEBUG(Lib_NpManager, "Signed out"); LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out");
return ORBIS_OK; return ORBIS_OK;
} }

View file

@ -32,6 +32,12 @@ struct OrbisNpId {
u8 reserved[8]; u8 reserved[8];
}; };
struct OrbisNpCountryCode {
char country_code[2];
char end;
char pad;
};
int PS4_SYSV_ABI Func_EF4378573542A508(); int PS4_SYSV_ABI Func_EF4378573542A508();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool(); 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 sceNpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountAge();
int PS4_SYSV_ABI sceNpGetAccountCountry(); 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 sceNpGetAccountDateOfBirth();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA();
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);

View file

@ -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.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z; pData[i].angularVelocity.z = states[i].angularVelocity.z;
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; 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(); const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) { if (gyro_poll_rate != 0.0f) {
GameController::CalculateOrientation(pData[i].acceleration, auto now = std::chrono::steady_clock::now();
pData[i].angularVelocity, float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
1.0f / gyro_poll_rate, pData[i].orientation); 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 = pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); (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].x = states[i].touchpad[0].x;
pData[i].touchData.touch[0].y = states[i].touchpad[0].y; 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].x = states[i].touchpad[1].x;
pData[i].touchData.touch[1].y = states[i].touchpad[1].y; 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].connected = connected;
pData[i].timestamp = states[i].time; pData[i].timestamp = states[i].time;
pData[i].connectedCount = connected_count; 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<int>(Input::Axis::LeftX)]; pData->leftStick.x = state.axes[static_cast<int>(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)]; pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)]; pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)]; pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)]; pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)]; pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
pData->acceleration.x = state.acceleration.x; pData->acceleration.x = state.acceleration.x * 0.098;
pData->acceleration.y = state.acceleration.y; pData->acceleration.y = state.acceleration.y * 0.098;
pData->acceleration.z = state.acceleration.z; pData->acceleration.z = state.acceleration.z * 0.098;
pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z; pData->angularVelocity.z = state.angularVelocity.z;
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f}; 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(); const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) { if (gyro_poll_rate != 0.0f) {
auto now = std::chrono::steady_clock::now();
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
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, 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 = pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); (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].x = state.touchpad[0].x;
pData->touchData.touch[0].y = state.touchpad[0].y; 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].x = state.touchpad[1].x;
pData->touchData.touch[1].y = state.touchpad[1].y; 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->timestamp = state.time;
pData->connected = true; // isConnected; //TODO fix me proper pData->connected = true; // isConnected; //TODO fix me proper
pData->connectedCount = 1; // connectedCount; pData->connectedCount = 1; // connectedCount;

View file

@ -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

View file

@ -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

View file

@ -117,6 +117,18 @@ void Linker::Execute(const std::vector<std::string> args) {
Common::SetCurrentThreadName("GAME_MainThread"); Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries(); 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<void*>(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. // Start main module.
EntryParams params{}; EntryParams params{};
params.argc = 1; params.argc = 1;

View file

@ -5,6 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h" #include "common/config.h"
#include "common/debug.h" #include "common/debug.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/process.h" #include "core/libraries/kernel/process.h"
@ -67,7 +68,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
} }
u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { 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. // Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer.
if (size < MinSizeToClamp) { if (size < MinSizeToClamp) {
return size; return size;
@ -181,6 +182,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
auto& area = CarveDmemArea(mapping_start, size)->second; auto& area = CarveDmemArea(mapping_start, size)->second;
area.memory_type = memory_type; area.memory_type = memory_type;
area.is_free = false; area.is_free = false;
MergeAdjacent(dmem_map, dmem_area);
return mapping_start; return mapping_start;
} }
@ -214,90 +216,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
MergeAdjacent(dmem_map, dmem_area); 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<void*>(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<void*>(mapped_addr);
return ORBIS_OK;
}
int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) { int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
@ -351,7 +269,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
return ORBIS_OK; 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, MemoryMapFlags flags, VMAType type, std::string_view name,
bool is_exec, PAddr phys_addr, u64 alignment) { bool is_exec, PAddr phys_addr, u64 alignment) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
@ -366,17 +284,18 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
// Fixed mapping means the virtual address must exactly match the provided one. // 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; 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. // 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. // To account for this, unmap any reserved areas within this mapping range first.
auto unmap_addr = mapped_addr; auto unmap_addr = mapped_addr;
auto unmap_size = size; auto unmap_size = size;
// If flag NoOverwrite is provided, don't overwrite mapped VMAs. // 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. // When it isn't provided, VMAs can be overwritten regardless of if they're mapped.
while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) && 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); auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
unmap_addr += unmapped; unmap_addr += unmapped;
unmap_size -= unmapped; unmap_size -= unmapped;
@ -384,51 +303,65 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
} }
vma = FindVMA(mapped_addr)->second; 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) { if (vma.IsMapped() || remaining_size < size) {
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr); LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
return ORBIS_KERNEL_ERROR_ENOMEM; return ORBIS_KERNEL_ERROR_ENOMEM;
} }
} } else {
// When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
// Find the first free area starting with provided virtual address. // search from address 0x200000000 instead.
if (False(flags & MemoryMapFlags::Fixed)) {
// Provided address needs to be aligned before we can map.
alignment = alignment > 0 ? alignment : 16_KB; 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) { if (mapped_addr == -1) {
// No suitable memory areas to map to // No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM; return ORBIS_KERNEL_ERROR_ENOMEM;
} }
} }
// Perform the mapping. // Create a memory area representing this mapping.
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); const auto new_vma_handle = CarveVMA(mapped_addr, size);
TRACK_ALLOC(*out_addr, size, "VMEM"); auto& new_vma = new_vma_handle->second;
auto& new_vma = CarveVMA(mapped_addr, size)->second; // If type is Flexible, we need to track how much flexible memory is used here.
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 == VMAType::Flexible) { if (type == VMAType::Flexible) {
flexible_usage += size; flexible_usage += size;
} }
if (IsValidGpuMapping(mapped_addr, size)) { new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
rasterizer->MapMemory(mapped_addr, size); new_vma.prot = prot;
new_vma.name = name;
new_vma.type = type;
new_vma.phys_base = phys_addr == -1 ? 0 : phys_addr;
new_vma.is_exec = is_exec;
if (type == VMAType::Reserved) {
// Technically this should be done for direct and flexible mappings too,
// But some Windows-specific limitations make that hard to accomplish.
MergeAdjacent(vma_map, new_vma_handle);
} }
if (type == VMAType::Reserved || type == VMAType::PoolReserved) {
// For Reserved/PoolReserved mappings, we don't perform any address space allocations.
// Just set out_addr to mapped_addr instead.
*out_addr = std::bit_cast<void*>(mapped_addr);
} else {
// If this is not a reservation, then map to GPU and address space
if (IsValidGpuMapping(mapped_addr, size)) {
rasterizer->MapMemory(mapped_addr, size);
}
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
}
TRACK_ALLOC(*out_addr, size, "VMEM");
return ORBIS_OK; return ORBIS_OK;
} }
int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, uintptr_t fd, size_t offset) { MemoryMapFlags flags, s32 fd, s64 phys_addr) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
const size_t size_aligned = Common::AlignUp(size, 16_KB); const size_t size_aligned = Common::AlignUp(size, 16_KB);
@ -449,8 +382,19 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
} }
// Map the file. // Get the file to map
impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast<u32>(prot), fd); 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<u32>(prot), handle);
if (prot >= MemoryProt::GpuRead) {
ASSERT_MSG(false, "Files cannot be mapped to GPU memory");
}
// Add virtual memory area // Add virtual memory area
auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second;
@ -485,14 +429,15 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
} }
if (type == VMAType::Pooled) { 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 // Track how much pooled memory is decommitted
pool_budget += size; pool_budget += size;
} }
if (IsValidGpuMapping(virtual_addr, size)) {
rasterizer->UnmapMemory(virtual_addr, size);
}
// Mark region as free and attempt to coalesce it with neighbours. // Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, size); const auto new_it = CarveVMA(virtual_addr, size);
auto& vma = new_it->second; auto& vma = new_it->second;
@ -528,18 +473,16 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
const auto adjusted_size = const auto adjusted_size =
vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : 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 bool has_backing = type == VMAType::Direct || type == VMAType::File;
const auto prot = vma_base.prot;
if (type == VMAType::Free) { if (type == VMAType::Free) {
return adjusted_size; return adjusted_size;
} }
if (type == VMAType::Flexible) { if (type == VMAType::Flexible) {
flexible_usage -= adjusted_size; flexible_usage -= adjusted_size;
} }
if (IsValidGpuMapping(virtual_addr, adjusted_size)) {
rasterizer->UnmapMemory(virtual_addr, adjusted_size);
}
// Mark region as free and attempt to coalesce it with neighbours. // Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, adjusted_size); const auto new_it = CarveVMA(virtual_addr, adjusted_size);
auto& vma = new_it->second; auto& vma = new_it->second;
@ -552,6 +495,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
auto& post_merge_vma = post_merge_it->second; auto& post_merge_vma = post_merge_it->second;
bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File; bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File;
if (type != VMAType::Reserved && type != VMAType::PoolReserved) { 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. // Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
phys_base, is_exec, has_backing, readonly_file); phys_base, is_exec, has_backing, readonly_file);
@ -605,20 +553,8 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size; vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size;
if (vma_base.type == VMAType::Free) { if (vma_base.type == VMAType::Free) {
LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region"); // On PS4, protecting freed memory does nothing.
return ORBIS_KERNEL_ERROR_EINVAL; 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 // Change protection
@ -650,11 +586,25 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex}; 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_addr = Common::AlignDown(addr, 16_KB);
auto aligned_size = Common::AlignUp(size + addr - aligned_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 it = FindVMA(aligned_addr + protected_bytes);
auto& vma_base = it->second; auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds", ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds",
@ -667,7 +617,7 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
return result; return result;
} }
protected_bytes += result; protected_bytes += result;
} while (protected_bytes < aligned_size); }
return ORBIS_OK; return ORBIS_OK;
} }
@ -798,12 +748,31 @@ s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) {
return ORBIS_OK; return ORBIS_OK;
} }
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) { void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) {
auto it = FindVMA(virtual_addr); // 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), auto it = FindVMA(aligned_addr);
"Range provided is not fully contained in vma"); s64 remaining_size = aligned_size;
it->second.name = name; 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 { void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const {
@ -824,6 +793,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment)
ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds", ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds",
virtual_addr); virtual_addr);
// Align up the virtual_addr first.
virtual_addr = Common::AlignUp(virtual_addr, alignment);
auto it = FindVMA(virtual_addr); auto it = FindVMA(virtual_addr);
// If the VMA is free and contains the requested mapping we are done. // If the VMA is free and contains the requested mapping we are done.

View file

@ -183,20 +183,14 @@ public:
void Free(PAddr phys_addr, size_t size); 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 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", MemoryMapFlags flags, VMAType type, std::string_view name = "anon",
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0); bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, uintptr_t fd, size_t offset); MemoryMapFlags flags, s32 fd, s64 phys_addr);
s32 PoolDecommit(VAddr virtual_addr, size_t size); s32 PoolDecommit(VAddr virtual_addr, size_t size);
@ -221,7 +215,7 @@ public:
s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type); 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; void InvalidateMemory(VAddr addr, u64 size) const;

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <filesystem>
#include <set> #include <set>
#include <fmt/core.h> #include <fmt/core.h>
@ -64,8 +65,13 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml"); Config::saveMainWindow(config_dir / "config.toml");
} }
void Emulator::Run(const std::filesystem::path& file, const std::vector<std::string> args) { void Emulator::Run(std::filesystem::path file, const std::vector<std::string> args) {
if (std::filesystem::is_directory(file)) {
file /= "eboot.bin";
}
const auto eboot_name = file.filename().string(); const auto eboot_name = file.filename().string();
auto game_folder = file.parent_path(); auto game_folder = file.parent_path();
if (const auto game_folder_name = game_folder.filename().string(); if (const auto game_folder_name = game_folder.filename().string();
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) { game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
@ -116,6 +122,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
Common::Log::Initialize(); Common::Log::Initialize();
} }
Common::Log::Start(); Common::Log::Start();
if (!std::filesystem::exists(file)) {
LOG_CRITICAL(Loader, "eboot.bin does not exist: {}",
std::filesystem::absolute(file).string());
std::quick_exit(0);
}
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version); LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version);
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev); LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
@ -196,15 +207,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
std::string window_title = ""; std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url); std::string remote_url(Common::g_scm_remote_url);
std::string remote_host; std::string remote_host = Common::GetRemoteNameFromLink();
try {
if (*remote_url.rbegin() == '/') {
remote_url.pop_back();
}
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
} catch (...) {
remote_host = "unknown";
}
if (Common::g_is_release) { if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title); window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
@ -260,7 +263,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
// Load the module with the linker // Load the module with the linker
const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name); const auto eboot_path = mnt->GetHostPath("/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 // check if we have system modules to load
LoadSystemModules(game_info.game_serial); LoadSystemModules(game_info.game_serial);

View file

@ -25,7 +25,7 @@ public:
Emulator(); Emulator();
~Emulator(); ~Emulator();
void Run(const std::filesystem::path& file, const std::vector<std::string> args = {}); void Run(std::filesystem::path file, const std::vector<std::string> args = {});
void UpdatePlayTime(const std::string& serial); void UpdatePlayTime(const std::string& serial);
private: private:

View file

@ -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 // SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
AddState(state); 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, void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity, Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime, float deltaTime,
Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation) { Libraries::Pad::OrbisFQuaternion& orientation) {
float ax = acceleration.x, ay = acceleration.y, az = acceleration.z; Libraries::Pad::OrbisFQuaternion q = lastOrientation;
float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z; 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.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 Libraries::Pad::OrbisFQuaternion qDot = {0.5f * .x, 0.5f * .y, 0.5f * .z, 0.5f * .w};
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;
// Estimated direction of gravity q.x += qDot.x * deltaTime;
float vx = 2.0f * (q2 * q4 - q1 * q3); q.y += qDot.y * deltaTime;
float vy = 2.0f * (q1 * q2 + q3 * q4); q.z += qDot.z * deltaTime;
float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; q.w += qDot.w * deltaTime;
// Error is cross product between estimated direction and measured direction of gravity float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
float ex = (ay * vz - az * vy); q.x /= norm;
float ey = (az * vx - ax * vz); q.y /= norm;
float ez = (ax * vy - ay * vx); q.z /= norm;
if (Ki > 0.0f) { q.w /= norm;
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
}
// Apply feedback terms orientation.x = q.x;
gx += Kp * ex + Ki * eInt[0]; orientation.y = q.y;
gy += Kp * ey + Ki * eInt[1]; orientation.z = q.z;
gz += Kp * ez + Ki * eInt[2]; orientation.w = q.w;
//// 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;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x, LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w); 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> engine) { void GameController::SetEngine(std::unique_ptr<Engine> engine) {
std::scoped_lock _{m_mutex}; std::scoped_lock _{m_mutex};
m_engine = std::move(engine); m_engine = std::move(engine);

View file

@ -23,6 +23,7 @@ enum class Axis {
}; };
struct TouchpadEntry { struct TouchpadEntry {
u8 ID = 0;
bool state{}; bool state{};
u16 x{}; u16 x{};
u16 y{}; u16 y{};
@ -82,9 +83,23 @@ public:
Engine* GetEngine(); Engine* GetEngine();
u32 Poll(); 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, static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity, Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime, float deltaTime,
Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation); Libraries::Pad::OrbisFQuaternion& orientation);
private: private:
@ -98,8 +113,15 @@ private:
int m_connected_count = 0; int m_connected_count = 0;
u32 m_states_num = 0; u32 m_states_num = 0;
u32 m_first_state = 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<State, MAX_STATES> m_states; std::array<State, MAX_STATES> m_states;
std::array<StateInternal, MAX_STATES> m_private; std::array<StateInternal, MAX_STATES> 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<Engine> m_engine = nullptr; std::unique_ptr<Engine> m_engine = nullptr;
}; };

View file

@ -56,15 +56,7 @@ bool MainWindow::Init() {
setMinimumSize(720, 405); setMinimumSize(720, 405);
std::string window_title = ""; std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url); std::string remote_url(Common::g_scm_remote_url);
std::string remote_host; std::string remote_host = Common::GetRemoteNameFromLink();
try {
if (*remote_url.rbegin() == '/') {
remote_url.pop_back();
}
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
} catch (...) {
remote_host = "unknown";
}
if (Common::g_is_release) { if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{}", Common::g_version); window_title = fmt::format("shadPS4 v{}", Common::g_version);
@ -380,16 +372,15 @@ void MainWindow::CreateConnects() {
connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable); connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable); connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable);
connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); 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->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons);
connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen); connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen);
connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
if (isTableList) { if (isTableList) {
m_game_list_frame->icon_size = m_game_list_frame->icon_size =
36 + value; // 36 is the minimum icon size to use due to text disappearing. 48 + value; // 48 is the minimum icon size to use due to text disappearing.
m_game_list_frame->ResizeIcons(36 + value); m_game_list_frame->ResizeIcons(48 + value);
Config::setIconSize(36 + value); Config::setIconSize(48 + value);
Config::setSliderPosition(value); Config::setSliderPosition(value);
} else { } else {
m_game_grid_frame->icon_size = 69 + value; m_game_grid_frame->icon_size = 69 + value;

View file

@ -29,7 +29,6 @@ class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
signals: signals:
void WindowResized(QResizeEvent* event); void WindowResized(QResizeEvent* event);
void ExtractionFinished();
public: public:
explicit MainWindow(QWidget* parent = nullptr); explicit MainWindow(QWidget* parent = nullptr);

View file

@ -1032,7 +1032,6 @@ void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) {
m_instruction.control.mimg = *reinterpret_cast<InstControlMIMG*>(&hexInstruction); m_instruction.control.mimg = *reinterpret_cast<InstControlMIMG*>(&hexInstruction);
m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode); m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode);
ASSERT(m_instruction.control.mimg.r128 == 0);
} }
void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) { void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) {

View file

@ -380,7 +380,7 @@ T Translator::GetSrc64(const InstOperand& operand) {
break; break;
case OperandField::VccLo: case OperandField::VccLo:
if constexpr (is_float) { if constexpr (is_float) {
UNREACHABLE(); value = ir.PackDouble2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi()));
} else { } else {
value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi())); value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi()));
} }

View file

@ -183,6 +183,7 @@ public:
void V_READFIRSTLANE_B32(const GcnInst& inst); void V_READFIRSTLANE_B32(const GcnInst& inst);
void V_CVT_I32_F64(const GcnInst& inst); void V_CVT_I32_F64(const GcnInst& inst);
void V_CVT_F64_I32(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_I32(const GcnInst& inst);
void V_CVT_F32_U32(const GcnInst& inst); void V_CVT_F32_U32(const GcnInst& inst);
void V_CVT_U32_F32(const GcnInst& inst); void V_CVT_U32_F32(const GcnInst& inst);

View file

@ -110,6 +110,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
return V_CVT_I32_F64(inst); return V_CVT_I32_F64(inst);
case Opcode::V_CVT_F64_I32: case Opcode::V_CVT_F64_I32:
return V_CVT_F64_I32(inst); return V_CVT_F64_I32(inst);
case Opcode::V_CVT_F64_U32:
return V_CVT_F64_U32(inst);
case Opcode::V_CVT_F32_I32: case Opcode::V_CVT_F32_I32:
return V_CVT_F32_I32(inst); return V_CVT_F32_I32(inst);
case Opcode::V_CVT_F32_U32: 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)); 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) { void Translator::V_CVT_F32_I32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src0{GetSrc(inst.src[0])};
SetDst(inst.dst[0], ir.ConvertSToF(32, 32, src0)); SetDst(inst.dst[0], ir.ConvertSToF(32, 32, src0));

View file

@ -152,6 +152,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
// Image gather operations // Image gather operations
case Opcode::IMAGE_GATHER4: case Opcode::IMAGE_GATHER4:
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_O: case Opcode::IMAGE_GATHER4_O:
@ -377,6 +378,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.has_lod.Assign(has_mip); info.has_lod.Assign(has_mip);
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info);
for (u32 i = 0; i < 4; i++) { for (u32 i = 0; i < 4; i++) {
@ -426,6 +428,7 @@ void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da); 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); const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info);
@ -451,6 +454,7 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value value = ir.GetVectorReg(val_reg); const IR::Value value = ir.GetVectorReg(val_reg);
const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg);
@ -509,6 +513,7 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal
info.has_lod.Assign(flags.any(MimgModifier::Lod)); info.has_lod.Assign(flags.any(MimgModifier::Lod));
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_unnormalized.Assign(mimg.unrm); info.is_unnormalized.Assign(mimg.unrm);
info.is_r128.Assign(mimg.r128);
if (gather) { if (gather) {
info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1);
@ -617,6 +622,7 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg);
const IR::Value body = ir.CompositeConstruct( const IR::Value body = ir.CompositeConstruct(

View file

@ -84,6 +84,7 @@ struct ImageResource {
bool is_atomic{}; bool is_atomic{};
bool is_array{}; bool is_array{};
bool is_written{}; bool is_written{};
bool is_r128{};
[[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; [[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 { constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept {
const auto image = info.ReadUdSharp<AmdGpu::Image>(sharp_idx); AmdGpu::Image image{0};
if (!is_r128) {
image = info.ReadUdSharp<AmdGpu::Image>(sharp_idx);
} else {
AmdGpu::Buffer buf = info.ReadUdSharp<AmdGpu::Buffer>(sharp_idx);
memcpy(&image, &buf, sizeof(buf));
}
if (!image.Valid()) { if (!image.Valid()) {
// Fall back to null image if unbound. // Fall back to null image if unbound.
return AmdGpu::Image::Null(); return AmdGpu::Image::Null();

View file

@ -411,6 +411,7 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
.is_atomic = IsImageAtomicInstruction(inst), .is_atomic = IsImageAtomicInstruction(inst),
.is_array = bool(inst_info.is_array), .is_array = bool(inst_info.is_array),
.is_written = is_written, .is_written = is_written,
.is_r128 = bool(inst_info.is_r128),
}); });
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};

View file

@ -44,6 +44,7 @@ union TextureInstInfo {
BitField<9, 1, u32> is_array; BitField<9, 1, u32> is_array;
BitField<10, 1, u32> is_unnormalized; BitField<10, 1, u32> is_unnormalized;
BitField<11, 1, u32> is_gather; BitField<11, 1, u32> is_gather;
BitField<12, 1, u32> is_r128;
}; };
union BufferInstInfo { union BufferInstInfo {

View file

@ -608,6 +608,16 @@ struct Liverpool {
} }
}; };
struct BorderColorBufferBase {
u32 base_addr_lo;
BitField<0, 8, u32> base_addr_hi;
template <typename T = VAddr>
T Address() const {
return std::bit_cast<T>(u64(base_addr_hi) << 40 | u64(base_addr_lo) << 8);
}
};
struct IndexBufferBase { struct IndexBufferBase {
BitField<0, 8, u32> base_addr_hi; BitField<0, 8, u32> base_addr_hi;
u32 base_addr_lo; u32 base_addr_lo;
@ -1299,7 +1309,9 @@ struct Liverpool {
Scissor screen_scissor; Scissor screen_scissor;
INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2); INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2);
DepthBuffer depth_buffer; 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; WindowOffset window_offset;
ViewportScissor window_scissor; ViewportScissor window_scissor;
INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2); 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(screen_scissor) == 0xA00C);
static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010); 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(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_offset) == 0xA080);
static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081); static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081);
static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E);

View file

@ -716,7 +716,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); 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); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
set_writes.push_back({ set_writes.push_back({
.dstSet = VK_NULL_HANDLE, .dstSet = VK_NULL_HANDLE,

View file

@ -9,7 +9,8 @@
namespace VideoCore { 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) { if (sampler.force_degamma) {
LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); 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 = const float maxAnisotropy =
anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()) anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy())
: 1.0f; : 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<vk::SamplerCustomBorderColorCreateInfoEXT> {
if (borderColor == vk::BorderColor::eFloatCustomEXT) {
const auto borderColorIndex = sampler.border_color_ptr.Value();
const auto borderColorBuffer = border_color_base.Address<std::array<float, 4>*>();
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 = { const vk::SamplerCreateInfo sampler_ci = {
.pNext = customColor ? &*customColor : nullptr,
.magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter),
.minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter), .minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter),
.mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_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), .compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func),
.minLod = sampler.MinLod(), .minLod = sampler.MinLod(),
.maxLod = sampler.MaxLod(), .maxLod = sampler.MaxLod(),
.borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type), .borderColor = borderColor,
.unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations. .unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations.
}; };
auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci);

View file

@ -14,7 +14,8 @@ namespace VideoCore {
class Sampler { class Sampler {
public: 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();
Sampler(const Sampler&) = delete; Sampler(const Sampler&) = delete;

View file

@ -636,9 +636,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
image.flags &= ~ImageFlagBits::Dirty; 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 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(); return it->second.Handle();
} }

View file

@ -139,7 +139,9 @@ public:
void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr); void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr);
/// Retrieves the sampler that matches the provided S# descriptor. /// 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. /// Retrieves the image with the specified id.
[[nodiscard]] Image& GetImage(ImageId id) { [[nodiscard]] Image& GetImage(ImageId id) {