Merge branch 'main' into tcb-swap

This commit is contained in:
squidbus 2025-05-15 15:53:30 -07:00 committed by GitHub
commit cac372eb01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 890 additions and 369 deletions

4
.gitmodules vendored
View File

@ -30,10 +30,6 @@
path = externals/xbyak path = externals/xbyak
url = https://github.com/herumi/xbyak.git url = https://github.com/herumi/xbyak.git
shallow = true shallow = true
[submodule "externals/winpthreads"]
path = externals/winpthreads
url = https://github.com/shadps4-emu/winpthreads.git
shallow = true
[submodule "externals/magic_enum"] [submodule "externals/magic_enum"]
path = externals/magic_enum path = externals/magic_enum
url = https://github.com/Neargye/magic_enum.git url = https://github.com/Neargye/magic_enum.git

View File

@ -54,9 +54,9 @@ else()
endif() endif()
if (ARCHITECTURE STREQUAL "x86_64") if (ARCHITECTURE STREQUAL "x86_64")
# Target the same CPU architecture as the PS4, to maintain the same level of compatibility. # Target x86-64-v3 CPU architecture as this is a good balance between supporting performance critical
# Exclude SSE4a as it is only available on AMD CPUs. # instructions like AVX2 and maintaining support for older CPUs.
add_compile_options(-march=btver2 -mtune=generic -mno-sse4a) add_compile_options(-march=x86-64-v3)
endif() endif()
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
@ -239,13 +239,6 @@ if (APPLE)
endif() endif()
list(POP_BACK CMAKE_MODULE_PATH) list(POP_BACK CMAKE_MODULE_PATH)
# Note: Windows always has these functions through winpthreads
include(CheckSymbolExists)
check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMEDLOCK)
if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32)
add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
# libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support. # libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support.
include(CheckCXXSymbolExists) include(CheckCXXSymbolExists)
@ -1094,11 +1087,15 @@ if (ENABLE_DISCORD_RPC)
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC) target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
endif() endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704 # Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD) if (ENABLE_USERFAULTFD)
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
endif() endif()
target_link_libraries(shadps4 PRIVATE uuid)
endif()
if (APPLE) if (APPLE)
# Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. # Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
if (ENABLE_QT_GUI) if (ENABLE_QT_GUI)
@ -1152,7 +1149,7 @@ if (ENABLE_QT_GUI)
endif() endif()
if (WIN32) if (WIN32)
target_link_libraries(shadps4 PRIVATE mincore winpthreads) target_link_libraries(shadps4 PRIVATE mincore)
if (MSVC) if (MSVC)
# MSVC likes putting opinions on what people can use, disable: # MSVC likes putting opinions on what people can use, disable:

View File

@ -21,9 +21,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
- A processor with at least 4 cores and 6 threads - A processor with at least 4 cores and 6 threads
- Above 2.5 GHz frequency - Above 2.5 GHz frequency
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM - A CPU supporting the x86-64-v3 baseline.
- **Intel**: Haswell generation or newer - **Intel**: Haswell generation or newer
- **AMD**: Jaguar generation or newer - **AMD**: Excavator generation or newer
- **Apple**: Rosetta 2 on macOS 15.4 or newer - **Apple**: Rosetta 2 on macOS 15.4 or newer
### GPU ### GPU

View File

@ -137,12 +137,6 @@ if (NOT TARGET Zydis::Zydis)
add_subdirectory(zydis) add_subdirectory(zydis)
endif() endif()
# Winpthreads
if (WIN32)
add_subdirectory(winpthreads)
target_include_directories(winpthreads INTERFACE winpthreads/include)
endif()
# sirit # sirit
add_subdirectory(sirit) add_subdirectory(sirit)
if (WIN32) if (WIN32)

@ -1 +0,0 @@
Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f

View File

@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <ctime>
#include <string> #include <string>
#include <thread> #include <thread>
@ -104,14 +105,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
SetThreadPriority(handle, windows_priority); SetThreadPriority(handle, windows_priority);
} }
static void AccurateSleep(std::chrono::nanoseconds duration) { bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
const bool interruptible) {
const auto begin_sleep = std::chrono::high_resolution_clock::now();
LARGE_INTEGER interval{ LARGE_INTEGER interval{
.QuadPart = -1 * (duration.count() / 100u), .QuadPart = -1 * (duration.count() / 100u),
}; };
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL); HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0); SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE); const auto ret = WaitForSingleObjectEx(timer, INFINITE, interruptible);
::CloseHandle(timer); ::CloseHandle(timer);
if (remaining) {
const auto end_sleep = std::chrono::high_resolution_clock::now();
const auto sleep_time = end_sleep - begin_sleep;
*remaining = duration > sleep_time ? duration - sleep_time : std::chrono::nanoseconds(0);
}
return ret == WAIT_OBJECT_0;
} }
#else #else
@ -134,8 +145,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
pthread_setschedparam(this_thread, scheduling_type, &params); pthread_setschedparam(this_thread, scheduling_type, &params);
} }
static void AccurateSleep(std::chrono::nanoseconds duration) { bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
std::this_thread::sleep_for(duration); const bool interruptible) {
timespec request = {
.tv_sec = duration.count() / 1'000'000'000,
.tv_nsec = duration.count() % 1'000'000'000,
};
timespec remain;
int ret;
while ((ret = nanosleep(&request, &remain)) < 0 && errno == EINTR) {
if (interruptible) {
break;
}
request = remain;
}
if (remaining) {
*remaining = std::chrono::nanoseconds(remain.tv_sec * 1'000'000'000 + remain.tv_nsec);
}
return ret == 0 || errno != EINTR;
} }
#endif #endif
@ -196,9 +223,9 @@ AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
: target_interval(target_interval) {} : target_interval(target_interval) {}
void AccurateTimer::Start() { void AccurateTimer::Start() {
auto begin_sleep = std::chrono::high_resolution_clock::now(); const auto begin_sleep = std::chrono::high_resolution_clock::now();
if (total_wait.count() > 0) { if (total_wait.count() > 0) {
AccurateSleep(total_wait); AccurateSleep(total_wait, nullptr, false);
} }
start_time = std::chrono::high_resolution_clock::now(); start_time = std::chrono::high_resolution_clock::now();
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep); total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);

View File

@ -25,6 +25,9 @@ void SetCurrentThreadName(const char* name);
void SetThreadName(void* thread, const char* name); void SetThreadName(void* thread, const char* name);
bool AccurateSleep(std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
bool interruptible);
class AccurateTimer { class AccurateTimer {
std::chrono::nanoseconds target_interval{}; std::chrono::nanoseconds target_interval{};
std::chrono::nanoseconds total_wait{}; std::chrono::nanoseconds total_wait{};

View File

@ -179,7 +179,7 @@ s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t add
auto* wait_reg_mem = reinterpret_cast<PM4CmdWaitRegMem*>(cmdbuf); auto* wait_reg_mem = reinterpret_cast<PM4CmdWaitRegMem*>(cmdbuf);
wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5}; wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5};
wait_reg_mem->raw = (is_mem << 4u) | (cmp_func & 7u); wait_reg_mem->raw = (is_mem << 4u) | (cmp_func & 7u);
wait_reg_mem->poll_addr_lo = u32(addr & addr_mask); wait_reg_mem->poll_addr_lo_raw = u32(addr & addr_mask);
wait_reg_mem->poll_addr_hi = u32(addr >> 32u); wait_reg_mem->poll_addr_hi = u32(addr >> 32u);
wait_reg_mem->ref = ref; wait_reg_mem->ref = ref;
wait_reg_mem->mask = mask; wait_reg_mem->mask = mask;

View File

@ -12,12 +12,25 @@
namespace Libraries::Kernel { namespace Libraries::Kernel {
extern boost::asio::io_context io_context;
extern void KernelSignalRequest();
static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
// Events are uniquely identified by id and filter. // Events are uniquely identified by id and filter.
bool EqueueInternal::AddEvent(EqueueEvent& event) { bool EqueueInternal::AddEvent(EqueueEvent& event) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
event.time_added = std::chrono::steady_clock::now(); event.time_added = std::chrono::steady_clock::now();
if (event.event.filter == SceKernelEvent::Filter::Timer ||
event.event.filter == SceKernelEvent::Filter::HrTimer) {
// HrTimer events are offset by the threshold of time at the end that we spinlock for
// greater accuracy.
const auto offset =
event.event.filter == SceKernelEvent::Filter::HrTimer ? HrTimerSpinlockThresholdUs : 0u;
event.timer_interval = std::chrono::microseconds(event.event.data - offset);
}
const auto& it = std::ranges::find(m_events, event); const auto& it = std::ranges::find(m_events, event);
if (it != m_events.cend()) { if (it != m_events.cend()) {
@ -29,6 +42,47 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) {
return true; return true;
} }
bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
void (*callback)(SceKernelEqueue, const SceKernelEvent&)) {
std::scoped_lock lock{m_mutex};
const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
return ev.event.ident == id && ev.event.filter == filter;
});
if (it == m_events.cend()) {
return false;
}
const auto& event = *it;
ASSERT(event.event.filter == SceKernelEvent::Filter::Timer ||
event.event.filter == SceKernelEvent::Filter::HrTimer);
if (!it->timer) {
it->timer = std::make_unique<boost::asio::steady_timer>(io_context, event.timer_interval);
} else {
// If the timer already exists we are scheduling a reoccurrence after the next period.
// Set the expiration time to the previous occurrence plus the period.
it->timer->expires_at(it->timer->expiry() + event.timer_interval);
}
it->timer->async_wait(
[this, event_data = event.event, callback](const boost::system::error_code& ec) {
if (ec) {
if (ec != boost::system::errc::operation_canceled) {
LOG_ERROR(Kernel_Event, "Timer callback error: {}", ec.message());
} else {
// Timer was cancelled (removed) before it triggered
LOG_DEBUG(Kernel_Event, "Timer cancelled");
}
return;
}
callback(this, event_data);
});
KernelSignalRequest();
return true;
}
bool EqueueInternal::RemoveEvent(u64 id, s16 filter) { bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
bool has_found = false; bool has_found = false;
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
@ -152,18 +206,14 @@ int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
return count; return count;
} }
extern boost::asio::io_context io_context; bool EqueueInternal::EventExists(u64 id, s16 filter) {
extern void KernelSignalRequest(); std::scoped_lock lock{m_mutex};
static constexpr auto HrTimerSpinlockThresholdUs = 1200u; const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
return ev.event.ident == id && ev.event.filter == filter;
});
static void SmallTimerCallback(const boost::system::error_code& error, SceKernelEqueue eq, return it != m_events.cend();
SceKernelEvent kevent) {
static EqueueEvent event;
event.event = kevent;
event.event.data = HrTimerSpinlockThresholdUs;
eq->AddSmallTimer(event);
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
} }
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) { int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
@ -243,6 +293,14 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
return ORBIS_OK; return ORBIS_OK;
} }
static void HrTimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
static EqueueEvent event;
event.event = kevent;
event.event.data = HrTimerSpinlockThresholdUs;
eq->AddSmallTimer(event);
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
}
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) { s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) {
if (eq == nullptr) { if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
@ -273,17 +331,10 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
} }
event.timer = std::make_unique<boost::asio::steady_timer>( if (!eq->AddEvent(event) ||
io_context, std::chrono::microseconds(total_us - HrTimerSpinlockThresholdUs)); !eq->ScheduleEvent(id, SceKernelEvent::Filter::HrTimer, HrTimerCallback)) {
event.timer->async_wait(std::bind(SmallTimerCallback, std::placeholders::_1, eq, event.event));
if (!eq->AddEvent(event)) {
return ORBIS_KERNEL_ERROR_ENOMEM; return ORBIS_KERNEL_ERROR_ENOMEM;
} }
KernelSignalRequest();
return ORBIS_OK; return ORBIS_OK;
} }
@ -300,6 +351,57 @@ int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) {
} }
} }
static void TimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
if (eq->EventExists(kevent.ident, kevent.filter)) {
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::Timer, kevent.udata);
if (!(kevent.flags & SceKernelEvent::Flags::OneShot)) {
// Reschedule the event for its next period.
eq->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback);
}
}
}
int PS4_SYSV_ABI sceKernelAddTimerEvent(SceKernelEqueue eq, int id, SceKernelUseconds usec,
void* udata) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
EqueueEvent event{};
event.event.ident = static_cast<u64>(id);
event.event.filter = SceKernelEvent::Filter::Timer;
event.event.flags = SceKernelEvent::Flags::Add;
event.event.fflags = 0;
event.event.data = usec;
event.event.udata = udata;
if (eq->EventExists(event.event.ident, event.event.filter)) {
eq->RemoveEvent(id, SceKernelEvent::Filter::Timer);
LOG_DEBUG(Kernel_Event,
"Timer event already exists, removing it: queue name={}, queue id={}",
eq->GetName(), event.event.ident);
}
LOG_DEBUG(Kernel_Event, "Added timing event: queue name={}, queue id={}, usec={}, pointer={:x}",
eq->GetName(), event.event.ident, usec, reinterpret_cast<uintptr_t>(udata));
if (!eq->AddEvent(event) ||
!eq->ScheduleEvent(id, SceKernelEvent::Filter::Timer, TimerCallback)) {
return ORBIS_KERNEL_ERROR_ENOMEM;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelDeleteTimerEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
return eq->RemoveEvent(id, SceKernelEvent::Filter::Timer) ? ORBIS_OK
: ORBIS_KERNEL_ERROR_ENOENT;
}
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) { int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) { if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
@ -380,6 +482,8 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent); LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent);
LIB_FUNCTION("57ZK+ODEXWY", "libkernel", 1, "libkernel", 1, 1, sceKernelAddTimerEvent);
LIB_FUNCTION("YWQFUyXIVdU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteTimerEvent);
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);

View File

@ -21,6 +21,9 @@ namespace Libraries::Kernel {
class EqueueInternal; class EqueueInternal;
struct EqueueEvent; struct EqueueEvent;
using SceKernelUseconds = u32;
using SceKernelEqueue = EqueueInternal*;
struct SceKernelEvent { struct SceKernelEvent {
enum Filter : s16 { enum Filter : s16 {
None = 0, None = 0,
@ -77,6 +80,7 @@ struct EqueueEvent {
SceKernelEvent event; SceKernelEvent event;
void* data = nullptr; void* data = nullptr;
std::chrono::steady_clock::time_point time_added; std::chrono::steady_clock::time_point time_added;
std::chrono::microseconds timer_interval;
std::unique_ptr<boost::asio::steady_timer> timer; std::unique_ptr<boost::asio::steady_timer> timer;
void ResetTriggerState() { void ResetTriggerState() {
@ -133,6 +137,8 @@ public:
} }
bool AddEvent(EqueueEvent& event); bool AddEvent(EqueueEvent& event);
bool ScheduleEvent(u64 id, s16 filter,
void (*callback)(SceKernelEqueue, const SceKernelEvent&));
bool RemoveEvent(u64 id, s16 filter); bool RemoveEvent(u64 id, s16 filter);
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
@ -152,6 +158,8 @@ public:
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
bool EventExists(u64 id, s16 filter);
private: private:
std::string m_name; std::string m_name;
std::mutex m_mutex; std::mutex m_mutex;
@ -160,9 +168,6 @@ private:
std::condition_variable m_cond; std::condition_variable m_cond;
}; };
using SceKernelUseconds = u32;
using SceKernelEqueue = EqueueInternal*;
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev); u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);

View File

@ -28,8 +28,12 @@
#ifdef _WIN64 #ifdef _WIN64
#include <Rpc.h> #include <Rpc.h>
#else
#include <uuid/uuid.h>
#endif #endif
#include <common/singleton.h> #include <common/singleton.h>
#include <core/libraries/network/net_error.h>
#include <core/libraries/network/sockets.h>
#include "aio.h" #include "aio.h"
namespace Libraries::Kernel { namespace Libraries::Kernel {
@ -104,6 +108,9 @@ void SetPosixErrno(int e) {
case EACCES: case EACCES:
g_posix_errno = POSIX_EACCES; g_posix_errno = POSIX_EACCES;
break; break;
case EFAULT:
g_posix_errno = POSIX_EFAULT;
break;
case EINVAL: case EINVAL:
g_posix_errno = POSIX_EINVAL; g_posix_errno = POSIX_EINVAL;
break; break;
@ -150,23 +157,23 @@ struct OrbisKernelUuid {
u8 clockSeqLow; u8 clockSeqLow;
u8 node[6]; u8 node[6];
}; };
static_assert(sizeof(OrbisKernelUuid) == 0x10);
int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
if (!orbisUuid) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
#ifdef _WIN64 #ifdef _WIN64
UUID uuid; UUID uuid;
UuidCreate(&uuid); if (UuidCreate(&uuid) != RPC_S_OK) {
orbisUuid->timeLow = uuid.Data1; return ORBIS_KERNEL_ERROR_EFAULT;
orbisUuid->timeMid = uuid.Data2;
orbisUuid->timeHiAndVersion = uuid.Data3;
orbisUuid->clockSeqHiAndReserved = uuid.Data4[0];
orbisUuid->clockSeqLow = uuid.Data4[1];
for (int i = 0; i < 6; i++) {
orbisUuid->node[i] = uuid.Data4[2 + i];
} }
#else #else
LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux"); uuid_t uuid;
uuid_generate(uuid);
#endif #endif
return 0; std::memcpy(orbisUuid, &uuid, sizeof(OrbisKernelUuid));
return ORBIS_OK;
} }
int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) { int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
@ -205,6 +212,24 @@ int PS4_SYSV_ABI posix_getpagesize() {
return 16_KB; return 16_KB;
} }
int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
Libraries::Net::OrbisNetSockaddr* addr, u32* paddrlen) {
auto* netcall = Common::Singleton<Libraries::Net::NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->GetSocketAddress(addr, paddrlen);
if (returncode >= 0) {
LOG_ERROR(Lib_Net, "return code : {:#x}", (u32)returncode);
return 0;
}
*Libraries::Kernel::__Error() = 0x20;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
void RegisterKernel(Core::Loader::SymbolsResolver* sym) { void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
service_thread = std::jthread{KernelServiceThread}; service_thread = std::jthread{KernelServiceThread};
@ -242,8 +267,7 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("lUk6wrGXyMw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom); LIB_FUNCTION("lUk6wrGXyMw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom);
LIB_FUNCTION("fFxGkxF2bVo", "libScePosix", 1, "libkernel", 1, 1, LIB_FUNCTION("fFxGkxF2bVo", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sys_setsockopt); Libraries::Net::sys_setsockopt);
LIB_FUNCTION("RenI1lL1WFk", "libScePosix", 1, "libkernel", 1, 1, // LIB_FUNCTION("RenI1lL1WFk", "libScePosix", 1, "libkernel", 1, 1, posix_getsockname);
Libraries::Net::sys_getsockname);
LIB_FUNCTION("KuOmgKoqCdY", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_bind); LIB_FUNCTION("KuOmgKoqCdY", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_bind);
LIB_FUNCTION("5jRCs2axtr4", "libScePosix", 1, "libkernel", 1, 1, LIB_FUNCTION("5jRCs2axtr4", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ... Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...

View File

@ -290,6 +290,12 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
directMemoryEndOut); directMemoryEndOut);
} }
int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end) {
LOG_DEBUG(Kernel_Vmm, "called, addr = {}", fmt::ptr(addr));
auto* memory = Core::Memory::Instance();
return memory->IsStack(std::bit_cast<VAddr>(addr), start, end);
}
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries, s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut) { int* numEntriesOut) {
return sceKernelBatchMap2(entries, numEntries, numEntriesOut, return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
@ -636,6 +642,7 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange); LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType); LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize); LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
LIB_FUNCTION("yDBwVAolDgg", "libkernel", 1, "libkernel", 1, 1, sceKernelIsStack);
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory); LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory); LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection); LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);

View File

@ -158,6 +158,7 @@ void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func[]);
int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut, int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
void** directMemoryStartOut, void** directMemoryStartOut,
void** directMemoryEndOut); void** directMemoryEndOut);
int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end);
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries, s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut); int* numEntriesOut);

View File

@ -17,6 +17,12 @@ int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr);
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr); int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr);
int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize,
Cpuset* cpusetp);
int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize,
const Cpuset* cpusetp);
int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr,
PthreadEntryFunc start_routine, void* arg); PthreadEntryFunc start_routine, void* arg);

View File

@ -315,7 +315,7 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
auto result = ef->Poll(bitPattern, wait, clear, pResultPat); auto result = ef->Poll(bitPattern, wait, clear, pResultPat);
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_EBUSY) { if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_EBUSY) {
LOG_ERROR(Kernel_Event, "returned {}", result); LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
} }
return result; return result;
@ -361,7 +361,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
u32 result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout); u32 result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout);
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_ETIMEDOUT) { if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_ETIMEDOUT) {
LOG_ERROR(Kernel_Event, "returned {:#x}", result); LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
} }
return result; return result;

View File

@ -6,6 +6,7 @@
#include "core/debug_state.h" #include "core/debug_state.h"
#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/posix_error.h"
#include "core/libraries/kernel/threads.h"
#include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/threads/pthread.h"
#include "core/libraries/kernel/threads/thread_state.h" #include "core/libraries/kernel/threads/thread_state.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -535,8 +536,6 @@ int Pthread::SetAffinity(const Cpuset* cpuset) {
return POSIX_EINVAL; return POSIX_EINVAL;
} }
u64 mask = cpuset->bits;
uintptr_t handle = native_thr.GetHandle(); uintptr_t handle = native_thr.GetHandle();
if (handle == 0) { if (handle == 0) {
return POSIX_ESRCH; return POSIX_ESRCH;
@ -545,6 +544,7 @@ int Pthread::SetAffinity(const Cpuset* cpuset) {
// We don't use this currently because some games gets performance problems // We don't use this currently because some games gets performance problems
// when applying affinity even on strong hardware // when applying affinity even on strong hardware
/* /*
u64 mask = cpuset->bits;
#ifdef _WIN64 #ifdef _WIN64
DWORD_PTR affinity_mask = static_cast<DWORD_PTR>(mask); DWORD_PTR affinity_mask = static_cast<DWORD_PTR>(mask);
if (!SetThreadAffinityMask(reinterpret_cast<HANDLE>(handle), affinity_mask)) { if (!SetThreadAffinityMask(reinterpret_cast<HANDLE>(handle), affinity_mask)) {
@ -572,13 +572,33 @@ int Pthread::SetAffinity(const Cpuset* cpuset) {
return 0; return 0;
} }
int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize, Cpuset* cpusetp) {
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
auto* attr_ptr = &thread->attr;
return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
}
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,
const Cpuset* cpusetp) { const Cpuset* cpusetp) {
if (thread == nullptr || cpusetp == nullptr) { if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL; return POSIX_EINVAL;
} }
thread->attr.cpusetsize = cpusetsize; auto* attr_ptr = &thread->attr;
return thread->SetAffinity(cpusetp); if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) {
return ret;
}
return thread->SetAffinity(thread->attr.cpuset);
}
int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {
Cpuset cpuset;
const int ret = posix_pthread_getaffinity_np(thread, sizeof(Cpuset), &cpuset);
if (ret == 0) {
*mask = cpuset.bits;
}
return ret;
} }
int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const u64 mask) { int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const u64 mask) {
@ -609,6 +629,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once); LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once);
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("Jb2uGFMr688", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getaffinity_np);
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setaffinity_np); LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setaffinity_np);
// Orbis // Orbis
@ -632,6 +653,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio)); LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio));
LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors);
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadGetaffinity));
LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadSetaffinity)); LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadSetaffinity));
} }

View File

@ -268,13 +268,13 @@ int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t c
attr->cpuset = static_cast<Cpuset*>(calloc(1, sizeof(Cpuset))); attr->cpuset = static_cast<Cpuset*>(calloc(1, sizeof(Cpuset)));
attr->cpusetsize = sizeof(Cpuset); attr->cpusetsize = sizeof(Cpuset);
} }
memcpy(attr->cpuset, cpusetp, cpusetsize); memcpy(attr->cpuset, cpusetp, std::min(cpusetsize, sizeof(Cpuset)));
return 0; return 0;
} }
int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, u64* mask) { int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* attr, u64* mask) {
Cpuset cpuset; Cpuset cpuset;
const int ret = posix_pthread_attr_getaffinity_np(param_1, sizeof(Cpuset), &cpuset); const int ret = posix_pthread_attr_getaffinity_np(attr, sizeof(Cpuset), &cpuset);
if (ret == 0) { if (ret == 0) {
*mask = cpuset.bits; *mask = cpuset.bits;
} }

View File

@ -5,24 +5,23 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/native_clock.h" #include "common/native_clock.h"
#include "common/thread.h"
#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/posix_error.h"
#include "core/libraries/kernel/time.h" #include "core/libraries/kernel/time.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#ifdef _WIN64 #ifdef _WIN64
#include <pthread_time.h>
#include <windows.h> #include <windows.h>
#include "common/ntapi.h" #include "common/ntapi.h"
#else #else
#if __APPLE__ #if __APPLE__
#include <date/tz.h> #include <date/tz.h>
#endif #endif
#include <ctime>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -52,88 +51,116 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
return clock->GetUptime(); return clock->GetUptime();
} }
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) { static s32 posix_nanosleep_impl(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp,
#ifdef _WIN64 const bool interruptible) {
const auto start_time = std::chrono::high_resolution_clock::now(); if (!rqtp || rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1'000'000'000) {
auto total_wait_time = std::chrono::microseconds(microseconds); SetPosixErrno(EINVAL);
return -1;
while (total_wait_time.count() > 0) {
auto wait_time = std::chrono::ceil<std::chrono::milliseconds>(total_wait_time).count();
u64 res = SleepEx(static_cast<u64>(wait_time), true);
if (res == WAIT_IO_COMPLETION) {
auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time;
auto elapsedMicroseconds =
std::chrono::duration_cast<std::chrono::microseconds>(elapsedTime).count();
total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds);
} else {
break;
} }
const auto duration = std::chrono::nanoseconds(rqtp->tv_sec * 1'000'000'000 + rqtp->tv_nsec);
std::chrono::nanoseconds remain;
const auto uninterrupted = Common::AccurateSleep(duration, &remain, interruptible);
if (rmtp) {
rmtp->tv_sec = remain.count() / 1'000'000'000;
rmtp->tv_nsec = remain.count() % 1'000'000'000;
} }
if (!uninterrupted) {
return 0; SetPosixErrno(EINTR);
#else return -1;
timespec start;
timespec remain;
start.tv_sec = microseconds / 1000000;
start.tv_nsec = (microseconds % 1000000) * 1000;
timespec* requested = &start;
int ret = 0;
do {
ret = nanosleep(requested, &remain);
requested = &remain;
} while (ret != 0);
return ret;
#endif
} }
int PS4_SYSV_ABI posix_usleep(u32 microseconds) {
return sceKernelUsleep(microseconds);
}
u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
std::this_thread::sleep_for(std::chrono::seconds(seconds));
return 0; return 0;
} }
#ifdef _WIN64 s32 PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
#ifndef CLOCK_REALTIME return posix_nanosleep_impl(rqtp, rmtp, true);
#define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_PROCESS_CPUTIME_ID
#define CLOCK_PROCESS_CPUTIME_ID 2
#endif
#ifndef CLOCK_THREAD_CPUTIME_ID
#define CLOCK_THREAD_CPUTIME_ID 3
#endif
#ifndef CLOCK_REALTIME_COARSE
#define CLOCK_REALTIME_COARSE 5
#endif
#ifndef CLOCK_MONOTONIC_COARSE
#define CLOCK_MONOTONIC_COARSE 6
#endif
#define DELTA_EPOCH_IN_100NS 116444736000000000ULL
static u64 FileTimeTo100Ns(FILETIME& ft) {
return *reinterpret_cast<u64*>(&ft);
} }
static s32 clock_gettime(u32 clock_id, struct timespec* ts) { s32 PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
if (const auto ret = posix_nanosleep_impl(rqtp, rmtp, false); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI posix_usleep(u32 microseconds) {
const OrbisKernelTimespec ts = {
.tv_sec = microseconds / 1'000'000,
.tv_nsec = (microseconds % 1'000'000) * 1'000,
};
return posix_nanosleep(&ts, nullptr);
}
s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
const OrbisKernelTimespec ts = {
.tv_sec = microseconds / 1'000'000,
.tv_nsec = (microseconds % 1'000'000) * 1'000,
};
return sceKernelNanosleep(&ts, nullptr);
}
u32 PS4_SYSV_ABI posix_sleep(u32 seconds) {
const OrbisKernelTimespec ts = {
.tv_sec = seconds,
.tv_nsec = 0,
};
OrbisKernelTimespec rm;
if (const auto ret = posix_nanosleep(&ts, &rm); ret < 0) {
return *__Error() == POSIX_EINTR ? rm.tv_sec + (rm.tv_nsec == 0 ? 0 : 1) : seconds;
}
return 0;
}
s32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
return sceKernelUsleep(seconds * 1'000'000);
}
s32 PS4_SYSV_ABI posix_clock_gettime(u32 clock_id, OrbisKernelTimespec* ts) {
if (ts == nullptr) {
SetPosixErrno(EFAULT);
return -1;
}
if (clock_id == ORBIS_CLOCK_PROCTIME) {
const auto us = sceKernelGetProcessTime();
ts->tv_sec = static_cast<s64>(us / 1'000'000);
ts->tv_nsec = static_cast<s64>((us % 1'000'000) * 1000);
return 0;
}
if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
clock_id = ORBIS_CLOCK_MONOTONIC;
}
#ifdef _WIN32
static const auto FileTimeTo100Ns = [](FILETIME& ft) { return *reinterpret_cast<u64*>(&ft); };
switch (clock_id) { switch (clock_id) {
case CLOCK_REALTIME: case ORBIS_CLOCK_REALTIME:
case CLOCK_REALTIME_COARSE: { case ORBIS_CLOCK_REALTIME_PRECISE: {
FILETIME ft; FILETIME ft;
GetSystemTimeAsFileTime(&ft); GetSystemTimePreciseAsFileTime(&ft);
const u64 ns = FileTimeTo100Ns(ft) - DELTA_EPOCH_IN_100NS; static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
ts->tv_sec = ns / 10'000'000; ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100; ts->tv_nsec = (ns % 10'000'000) * 100;
return 0; return 0;
} }
case CLOCK_MONOTONIC: case ORBIS_CLOCK_SECOND:
case CLOCK_MONOTONIC_COARSE: { case ORBIS_CLOCK_REALTIME_FAST: {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
case ORBIS_CLOCK_UPTIME:
case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST: {
static LARGE_INTEGER pf = [] { static LARGE_INTEGER pf = [] {
LARGE_INTEGER res{}; LARGE_INTEGER res{};
QueryPerformanceFrequency(&pf); QueryPerformanceFrequency(&pf);
@ -141,43 +168,53 @@ static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
}(); }();
LARGE_INTEGER pc{}; LARGE_INTEGER pc{};
QueryPerformanceCounter(&pc); if (!QueryPerformanceCounter(&pc)) {
SetPosixErrno(EFAULT);
return -1;
}
ts->tv_sec = pc.QuadPart / pf.QuadPart; ts->tv_sec = pc.QuadPart / pf.QuadPart;
ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart; ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart;
return 0; return 0;
} }
case CLOCK_PROCESS_CPUTIME_ID: { case ORBIS_CLOCK_THREAD_CPUTIME_ID: {
FILETIME ct, et, kt, ut; FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) { if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
return EFAULT; SetPosixErrno(EFAULT);
return -1;
} }
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt); const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000; ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100; ts->tv_nsec = (ns % 10'000'000) * 100;
return 0; return 0;
} }
case CLOCK_THREAD_CPUTIME_ID: { case ORBIS_CLOCK_VIRTUAL: {
FILETIME ct, et, kt, ut; FILETIME ct, et, kt, ut;
if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) { if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
return EFAULT; SetPosixErrno(EFAULT);
return -1;
} }
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt); const u64 ns = FileTimeTo100Ns(ut);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
case ORBIS_CLOCK_PROF: {
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
SetPosixErrno(EFAULT);
return -1;
}
const u64 ns = FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000; ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100; ts->tv_nsec = (ns % 10'000'000) * 100;
return 0; return 0;
} }
default: default:
return EINVAL; SetPosixErrno(EFAULT);
return -1;
} }
} #else
#endif clockid_t pclock_id;
int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* ts) {
if (ts == nullptr) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
clockid_t pclock_id = CLOCK_MONOTONIC;
switch (clock_id) { switch (clock_id) {
case ORBIS_CLOCK_REALTIME: case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE: case ORBIS_CLOCK_REALTIME_PRECISE:
@ -185,7 +222,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
break; break;
case ORBIS_CLOCK_SECOND: case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST: case ORBIS_CLOCK_REALTIME_FAST:
#ifndef __APPLE__ #ifdef CLOCK_REALTIME_COARSE
pclock_id = CLOCK_REALTIME_COARSE; pclock_id = CLOCK_REALTIME_COARSE;
#else #else
pclock_id = CLOCK_REALTIME; pclock_id = CLOCK_REALTIME;
@ -199,7 +236,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
break; break;
case ORBIS_CLOCK_UPTIME_FAST: case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST: case ORBIS_CLOCK_MONOTONIC_FAST:
#ifndef __APPLE__ #ifdef CLOCK_MONOTONIC_COARSE
pclock_id = CLOCK_MONOTONIC_COARSE; pclock_id = CLOCK_MONOTONIC_COARSE;
#else #else
pclock_id = CLOCK_MONOTONIC; pclock_id = CLOCK_MONOTONIC;
@ -208,102 +245,155 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
case ORBIS_CLOCK_THREAD_CPUTIME_ID: case ORBIS_CLOCK_THREAD_CPUTIME_ID:
pclock_id = CLOCK_THREAD_CPUTIME_ID; pclock_id = CLOCK_THREAD_CPUTIME_ID;
break; break;
case ORBIS_CLOCK_PROCTIME: {
const auto us = sceKernelGetProcessTime();
ts->tv_sec = us / 1'000'000;
ts->tv_nsec = (us % 1'000'000) * 1000;
return 0;
}
case ORBIS_CLOCK_VIRTUAL: { case ORBIS_CLOCK_VIRTUAL: {
#ifdef _WIN64 rusage ru;
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
return EFAULT;
}
const u64 ns = FileTimeTo100Ns(ut);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
#else
struct rusage ru;
const auto res = getrusage(RUSAGE_SELF, &ru); const auto res = getrusage(RUSAGE_SELF, &ru);
if (res < 0) { if (res < 0) {
return res; SetPosixErrno(EFAULT);
return -1;
} }
ts->tv_sec = ru.ru_utime.tv_sec; ts->tv_sec = ru.ru_utime.tv_sec;
ts->tv_nsec = ru.ru_utime.tv_usec * 1000; ts->tv_nsec = ru.ru_utime.tv_usec * 1000;
#endif
return 0; return 0;
} }
case ORBIS_CLOCK_PROF: { case ORBIS_CLOCK_PROF: {
#ifdef _WIN64 rusage ru;
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
return EFAULT;
}
const u64 ns = FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
#else
struct rusage ru;
const auto res = getrusage(RUSAGE_SELF, &ru); const auto res = getrusage(RUSAGE_SELF, &ru);
if (res < 0) { if (res < 0) {
return res; SetPosixErrno(EFAULT);
return -1;
} }
ts->tv_sec = ru.ru_stime.tv_sec; ts->tv_sec = ru.ru_stime.tv_sec;
ts->tv_nsec = ru.ru_stime.tv_usec * 1000; ts->tv_nsec = ru.ru_stime.tv_usec * 1000;
#endif
return 0; return 0;
} }
case ORBIS_CLOCK_EXT_NETWORK:
case ORBIS_CLOCK_EXT_DEBUG_NETWORK:
case ORBIS_CLOCK_EXT_AD_NETWORK:
case ORBIS_CLOCK_EXT_RAW_NETWORK:
pclock_id = CLOCK_MONOTONIC;
LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_MONOTONIC", clock_id);
break;
default: default:
return EINVAL; SetPosixErrno(EFAULT);
return -1;
} }
timespec t{}; timespec t{};
int result = clock_gettime(pclock_id, &t); const auto result = clock_gettime(pclock_id, &t);
ts->tv_sec = t.tv_sec; ts->tv_sec = t.tv_sec;
ts->tv_nsec = t.tv_nsec; ts->tv_nsec = t.tv_nsec;
return result; if (result < 0) {
SetPosixErrno(errno);
return -1;
}
return 0;
#endif
} }
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { s32 PS4_SYSV_ABI sceKernelClockGettime(const u32 clock_id, OrbisKernelTimespec* ts) {
const auto res = orbis_clock_gettime(clock_id, tp); if (const auto ret = posix_clock_gettime(clock_id, ts); ret < 0) {
if (res < 0) { return ErrnoToSceKernelError(*__Error());
return ErrnoToSceKernelError(res);
} }
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) { s32 PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
const auto* request = reinterpret_cast<const timespec*>(rqtp); if (res == nullptr) {
auto* remain = reinterpret_cast<timespec*>(rmtp); SetPosixErrno(EFAULT);
return nanosleep(request, remain); return -1;
} }
int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) { if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
if (!rqtp || !rmtp) { clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
return ORBIS_KERNEL_ERROR_EFAULT; LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
clock_id = ORBIS_CLOCK_MONOTONIC;
} }
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) { #ifdef _WIN32
return ORBIS_KERNEL_ERROR_EINVAL; switch (clock_id) {
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST: {
DWORD timeAdjustment;
DWORD timeIncrement;
BOOL isTimeAdjustmentDisabled;
if (!GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled)) {
SetPosixErrno(EFAULT);
return -1;
}
res->tv_sec = 0;
res->tv_nsec = timeIncrement * 100;
return 0;
}
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
case ORBIS_CLOCK_UPTIME:
case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST: {
LARGE_INTEGER pf;
if (!QueryPerformanceFrequency(&pf)) {
SetPosixErrno(EFAULT);
return -1;
}
res->tv_sec = 0;
res->tv_nsec =
std::max(static_cast<s32>((1000000000 + (pf.QuadPart >> 1)) / pf.QuadPart), 1);
return 0;
}
default:
UNREACHABLE();
}
#else
clockid_t pclock_id;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
pclock_id = CLOCK_REALTIME;
break;
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST:
#ifdef CLOCK_REALTIME_COARSE
pclock_id = CLOCK_REALTIME_COARSE;
#else
pclock_id = CLOCK_REALTIME;
#endif
break;
case ORBIS_CLOCK_UPTIME:
case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
pclock_id = CLOCK_MONOTONIC;
break;
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST:
#ifdef CLOCK_MONOTONIC_COARSE
pclock_id = CLOCK_MONOTONIC_COARSE;
#else
pclock_id = CLOCK_MONOTONIC;
#endif
break;
default:
UNREACHABLE();
} }
return posix_nanosleep(rqtp, rmtp); timespec t{};
const auto result = clock_getres(pclock_id, &t);
res->tv_sec = t.tv_sec;
res->tv_nsec = t.tv_nsec;
if (result < 0) {
SetPosixErrno(errno);
return -1;
}
return 0;
#endif
} }
int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { s32 PS4_SYSV_ABI sceKernelClockGetres(const u32 clock_id, OrbisKernelTimespec* res) {
if (!tp) { if (const auto ret = posix_clock_getres(clock_id, res); ret < 0) {
return ORBIS_KERNEL_ERROR_EFAULT; return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
} }
s32 PS4_SYSV_ABI posix_gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
#ifdef _WIN64 #ifdef _WIN64
if (tp) {
FILETIME filetime; FILETIME filetime;
GetSystemTimePreciseAsFileTime(&filetime); GetSystemTimePreciseAsFileTime(&filetime);
@ -318,28 +408,8 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
tp->tv_sec = ticks / TICKS_PER_SECOND; tp->tv_sec = ticks / TICKS_PER_SECOND;
tp->tv_usec = ticks % TICKS_PER_SECOND; tp->tv_usec = ticks % TICKS_PER_SECOND;
#else
timeval tv;
gettimeofday(&tv, nullptr);
tp->tv_sec = tv.tv_sec;
tp->tv_usec = tv.tv_usec;
#endif
return ORBIS_OK;
} }
int PS4_SYSV_ABI gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
// FreeBSD docs mention that the kernel generally does not track these values
// and they are usually returned as zero.
if (tz) { if (tz) {
tz->tz_minuteswest = 0;
tz->tz_dsttime = 0;
}
return sceKernelGettimeofday(tp);
}
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
#ifdef _WIN64
ASSERT(tz);
static int tzflag = 0; static int tzflag = 0;
if (!tzflag) { if (!tzflag) {
_tzset(); _tzset();
@ -347,57 +417,54 @@ s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
} }
tz->tz_minuteswest = _timezone / 60; tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight; tz->tz_dsttime = _daylight;
}
return 0;
#else #else
struct timezone tzz; struct timezone tzz;
struct timeval tv; timeval tv;
gettimeofday(&tv, &tzz); const auto ret = gettimeofday(&tv, &tzz);
if (tp) {
tp->tv_sec = tv.tv_sec;
tp->tv_usec = tv.tv_usec;
}
if (tz) {
tz->tz_dsttime = tzz.tz_dsttime; tz->tz_dsttime = tzz.tz_dsttime;
tz->tz_minuteswest = tzz.tz_minuteswest; tz->tz_minuteswest = tzz.tz_minuteswest;
}
if (ret < 0) {
SetPosixErrno(errno);
return -1;
}
return 0;
#endif #endif
}
s32 PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
if (const auto ret = posix_gettimeofday(tp, nullptr); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) { s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
if (res == nullptr) { if (const auto ret = posix_gettimeofday(nullptr, tz); ret < 0) {
return ORBIS_KERNEL_ERROR_EFAULT; return ErrnoToSceKernelError(*__Error());
} }
clockid_t pclock_id = CLOCK_REALTIME;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
case ORBIS_CLOCK_REALTIME_FAST:
pclock_id = CLOCK_REALTIME;
break;
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
case ORBIS_CLOCK_MONOTONIC_FAST:
pclock_id = CLOCK_MONOTONIC;
break;
default:
UNREACHABLE();
}
timespec t{};
int result = clock_getres(pclock_id, &t);
res->tv_sec = t.tv_sec;
res->tv_nsec = t.tv_nsec;
if (result == 0) {
return ORBIS_OK; return ORBIS_OK;
} }
return ORBIS_KERNEL_ERROR_EINVAL;
}
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds, s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, int* dst_seconds) { OrbisKernelTimezone* timezone, s32* dst_seconds) {
LOG_INFO(Kernel, "called"); LOG_INFO(Kernel, "called");
if (timezone) { if (timezone) {
sceKernelGettimezone(timezone); sceKernelGettimezone(timezone);
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60; param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
if (seconds) if (seconds) {
*seconds = param_1; *seconds = param_1;
if (dst_seconds) }
if (dst_seconds) {
*dst_seconds = timezone->tz_dsttime * 60; *dst_seconds = timezone->tz_dsttime * 60;
}
} else { } else {
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
@ -415,7 +482,7 @@ Common::NativeClock* GetClock() {
} // namespace Dev } // namespace Dev
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
struct OrbisTimesec* st, u64* dst_sec) { struct OrbisTimesec* st, u64* dst_sec) {
LOG_TRACE(Kernel, "Called"); LOG_TRACE(Kernel, "Called");
#ifdef __APPLE__ #ifdef __APPLE__
@ -444,28 +511,35 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
void RegisterTime(Core::Loader::SymbolsResolver* sym) { void RegisterTime(Core::Loader::SymbolsResolver* sym) {
clock = std::make_unique<Common::NativeClock>(); clock = std::make_unique<Common::NativeClock>();
initial_ptc = clock->GetUptime(); initial_ptc = clock->GetUptime();
// POSIX
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("0wu33hunNdE", "libkernel", 1, "libkernel", 1, 1, posix_sleep);
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, posix_sleep);
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime);
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
LIB_FUNCTION("smIj7eqzZE8", "libkernel", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, posix_gettimeofday);
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, posix_gettimeofday);
// Orbis
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime); LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter); LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter);
LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1,
sceKernelGetProcessTimeCounterFrequency); sceKernelGetProcessTimeCounterFrequency);
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc); LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency); LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency);
LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, gettimeofday);
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, gettimeofday);
LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep); LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep);
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep); LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep); LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep);
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, sceKernelSleep);
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime); LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime);
LIB_FUNCTION("wRYVA5Zolso", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGetres);
LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone); LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone);
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, orbis_clock_gettime);
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, orbis_clock_gettime);
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc); LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
} }

View File

@ -75,14 +75,14 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
u64 PS4_SYSV_ABI sceKernelReadTsc(); u64 PS4_SYSV_ABI sceKernelReadTsc();
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp); s32 PS4_SYSV_ABI sceKernelClockGettime(u32 clock_id, OrbisKernelTimespec* tp);
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz); s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds, s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, int* dst_seconds); OrbisKernelTimezone* timezone, s32* dst_seconds);
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st, s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
u64* dst_sec); u64* dst_sec);
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds); s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
void RegisterTime(Core::Loader::SymbolsResolver* sym); void RegisterTime(Core::Loader::SymbolsResolver* sym);

View File

@ -886,6 +886,7 @@ int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* pa
} }
int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) { int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) {
LOG_INFO(Lib_Net, "s={} level={} optname={}", s, level, optname);
if (!g_isNetInitialized) { if (!g_isNetInitialized) {
return ORBIS_NET_ERROR_ENOTINIT; return ORBIS_NET_ERROR_ENOTINIT;
} }
@ -1449,6 +1450,7 @@ int PS4_SYSV_ABI sceNetSetDnsInfoToKernel() {
int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval, int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval,
u32 optlen) { u32 optlen) {
LOG_INFO(Lib_Net, "s={} level={} optname={} optlen={}", s, level, optname, optlen);
if (!g_isNetInitialized) { if (!g_isNetInitialized) {
return ORBIS_NET_ERROR_ENOTINIT; return ORBIS_NET_ERROR_ENOTINIT;
} }

View File

@ -10,25 +10,25 @@ namespace Libraries::Net {
int P2PSocket::Close() { int P2PSocket::Close() {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
int P2PSocket::Listen(int backlog) { int P2PSocket::Listen(int backlog) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
@ -49,12 +49,12 @@ SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1; return 0;
} }
} // namespace Libraries::Net } // namespace Libraries::Net

View File

@ -143,6 +143,7 @@ static void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) {
} }
int PosixSocket::Close() { int PosixSocket::Close() {
std::scoped_lock lock{m_mutex};
#ifdef _WIN32 #ifdef _WIN32
auto out = closesocket(sock); auto out = closesocket(sock);
#else #else
@ -152,17 +153,20 @@ int PosixSocket::Close() {
} }
int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
std::scoped_lock lock{m_mutex};
sockaddr addr2; sockaddr addr2;
convertOrbisNetSockaddrToPosix(addr, &addr2); convertOrbisNetSockaddrToPosix(addr, &addr2);
return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in))); return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in)));
} }
int PosixSocket::Listen(int backlog) { int PosixSocket::Listen(int backlog) {
std::scoped_lock lock{m_mutex};
return ConvertReturnErrorCode(::listen(sock, backlog)); return ConvertReturnErrorCode(::listen(sock, backlog));
} }
int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) { u32 tolen) {
std::scoped_lock lock{m_mutex};
if (to != nullptr) { if (to != nullptr) {
sockaddr addr; sockaddr addr;
convertOrbisNetSockaddrToPosix(to, &addr); convertOrbisNetSockaddrToPosix(to, &addr);
@ -175,6 +179,7 @@ int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetS
int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from,
u32* fromlen) { u32* fromlen) {
std::scoped_lock lock{m_mutex};
if (from != nullptr) { if (from != nullptr) {
sockaddr addr; sockaddr addr;
int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen); int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen);
@ -187,6 +192,7 @@ int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr*
} }
SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
std::scoped_lock lock{m_mutex};
sockaddr addr2; sockaddr addr2;
net_socket new_socket = ::accept(sock, &addr2, (socklen_t*)addrlen); net_socket new_socket = ::accept(sock, &addr2, (socklen_t*)addrlen);
#ifdef _WIN32 #ifdef _WIN32
@ -202,12 +208,14 @@ SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
} }
int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
std::scoped_lock lock{m_mutex};
sockaddr addr2; sockaddr addr2;
convertOrbisNetSockaddrToPosix(addr, &addr2); convertOrbisNetSockaddrToPosix(addr, &addr2);
return ::connect(sock, &addr2, sizeof(sockaddr_in)); return ::connect(sock, &addr2, sizeof(sockaddr_in));
} }
int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
std::scoped_lock lock{m_mutex};
sockaddr addr; sockaddr addr;
convertOrbisNetSockaddrToPosix(name, &addr); convertOrbisNetSockaddrToPosix(name, &addr);
if (name != nullptr) { if (name != nullptr) {
@ -234,13 +242,15 @@ int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
return 0 return 0
int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
std::scoped_lock lock{m_mutex};
level = ConvertLevels(level); level = ConvertLevels(level);
::linger native_linger;
if (level == SOL_SOCKET) { if (level == SOL_SOCKET) {
switch (optname) { switch (optname) {
CASE_SETSOCKOPT(SO_REUSEADDR); CASE_SETSOCKOPT(SO_REUSEADDR);
CASE_SETSOCKOPT(SO_KEEPALIVE); CASE_SETSOCKOPT(SO_KEEPALIVE);
CASE_SETSOCKOPT(SO_BROADCAST); CASE_SETSOCKOPT(SO_BROADCAST);
CASE_SETSOCKOPT(SO_LINGER); // CASE_SETSOCKOPT(SO_LINGER);
CASE_SETSOCKOPT(SO_SNDBUF); CASE_SETSOCKOPT(SO_SNDBUF);
CASE_SETSOCKOPT(SO_RCVBUF); CASE_SETSOCKOPT(SO_RCVBUF);
CASE_SETSOCKOPT(SO_SNDTIMEO); CASE_SETSOCKOPT(SO_SNDTIMEO);
@ -251,6 +261,24 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, &sockopt_so_onesbcast); CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, &sockopt_so_onesbcast);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto); CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature); CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature);
case ORBIS_NET_SO_LINGER: {
if (socket_type != ORBIS_NET_SOCK_STREAM) {
return ORBIS_NET_EPROCUNAVAIL;
}
if (optlen < sizeof(OrbisNetLinger)) {
LOG_ERROR(Lib_Net, "size missmatched! optlen = {} OrbisNetLinger={}", optlen,
sizeof(OrbisNetLinger));
return ORBIS_NET_ERROR_EINVAL;
}
const void* native_val = &native_linger;
u32 native_len = sizeof(native_linger);
native_linger.l_onoff = reinterpret_cast<const OrbisNetLinger*>(optval)->l_onoff;
native_linger.l_linger = reinterpret_cast<const OrbisNetLinger*>(optval)->l_linger;
return ConvertReturnErrorCode(
setsockopt(sock, level, SO_LINGER, (const char*)native_val, native_len));
}
case ORBIS_NET_SO_NAME: case ORBIS_NET_SO_NAME:
return ORBIS_NET_ERROR_EINVAL; // don't support set for name return ORBIS_NET_ERROR_EINVAL; // don't support set for name
case ORBIS_NET_SO_NBIO: { case ORBIS_NET_SO_NBIO: {
@ -269,7 +297,7 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
} }
} else if (level == IPPROTO_IP) { } else if (level == IPPROTO_IP) {
switch (optname) { switch (optname) {
CASE_SETSOCKOPT(IP_HDRINCL); // CASE_SETSOCKOPT(IP_HDRINCL);
CASE_SETSOCKOPT(IP_TOS); CASE_SETSOCKOPT(IP_TOS);
CASE_SETSOCKOPT(IP_TTL); CASE_SETSOCKOPT(IP_TTL);
CASE_SETSOCKOPT(IP_MULTICAST_IF); CASE_SETSOCKOPT(IP_MULTICAST_IF);
@ -279,6 +307,13 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP); CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk); CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl); CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl);
case ORBIS_NET_IP_HDRINCL: {
if (socket_type != ORBIS_NET_SOCK_RAW) {
return ORBIS_NET_EPROCUNAVAIL;
}
return ConvertReturnErrorCode(
setsockopt(sock, level, optname, (const char*)optval, optlen));
}
} }
} else if (level == IPPROTO_TCP) { } else if (level == IPPROTO_TCP) {
switch (optname) { switch (optname) {
@ -311,6 +346,7 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
return 0; return 0;
int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
std::scoped_lock lock{m_mutex};
level = ConvertLevels(level); level = ConvertLevels(level);
if (level == SOL_SOCKET) { if (level == SOL_SOCKET) {
switch (optname) { switch (optname) {

View File

@ -32,6 +32,10 @@ struct Socket;
typedef std::shared_ptr<Socket> SocketPtr; typedef std::shared_ptr<Socket> SocketPtr;
struct OrbisNetLinger {
s32 l_onoff;
s32 l_linger;
};
struct Socket { struct Socket {
explicit Socket(int domain, int type, int protocol) {} explicit Socket(int domain, int type, int protocol) {}
virtual ~Socket() = default; virtual ~Socket() = default;
@ -47,6 +51,7 @@ struct Socket {
u32* fromlen) = 0; u32* fromlen) = 0;
virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0; virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0;
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0; virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
std::mutex m_mutex;
}; };
struct PosixSocket : public Socket { struct PosixSocket : public Socket {
@ -59,8 +64,11 @@ struct PosixSocket : public Socket {
int sockopt_ip_ttlchk = 0; int sockopt_ip_ttlchk = 0;
int sockopt_ip_maxttl = 0; int sockopt_ip_maxttl = 0;
int sockopt_tcp_mss_to_advertise = 0; int sockopt_tcp_mss_to_advertise = 0;
int socket_type;
explicit PosixSocket(int domain, int type, int protocol) explicit PosixSocket(int domain, int type, int protocol)
: Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {} : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {
socket_type = type;
}
explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {} explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {}
int Close() override; int Close() override;
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;

View File

@ -1,4 +1,3 @@
#include "sys_net.h"
// 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

View File

@ -380,8 +380,7 @@ s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener,
s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle, s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle,
u32 numSpeakers) { u32 numSpeakers) {
LOG_ERROR(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle, LOG_ERROR(Lib_Ngs2, "unitAngle = {}, numSpeakers = {}", unitAngle, numSpeakers);
unitAngle, numSpeakers);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -649,19 +649,23 @@ 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; s64 protected_bytes = 0;
auto aligned_addr = Common::AlignDown(addr, 16_KB);
auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB);
do { do {
auto it = FindVMA(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",
addr + protected_bytes); addr + protected_bytes);
auto result = 0; auto result = 0;
result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot); result = ProtectBytes(aligned_addr + protected_bytes, vma_base,
aligned_size - protected_bytes, prot);
if (result < 0) { if (result < 0) {
// ProtectBytes returned an error, return it // ProtectBytes returned an error, return it
return result; return result;
} }
protected_bytes += result; protected_bytes += result;
} while (protected_bytes < size); } while (protected_bytes < aligned_size);
return ORBIS_OK; return ORBIS_OK;
} }
@ -945,4 +949,33 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
return ORBIS_OK; return ORBIS_OK;
} }
int MemoryManager::IsStack(VAddr addr, void** start, void** end) {
auto vma_handle = FindVMA(addr);
if (vma_handle == vma_map.end()) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
const VirtualMemoryArea& vma = vma_handle->second;
if (!vma.Contains(addr, 0) || vma.IsFree()) {
return ORBIS_KERNEL_ERROR_EACCES;
}
auto stack_start = 0ul;
auto stack_end = 0ul;
if (vma.type == VMAType::Stack) {
stack_start = vma.base;
stack_end = vma.base + vma.size;
}
if (start != nullptr) {
*start = reinterpret_cast<void*>(stack_start);
}
if (end != nullptr) {
*end = reinterpret_cast<void*>(stack_end);
}
return ORBIS_OK;
}
} // namespace Core } // namespace Core

View File

@ -223,6 +223,8 @@ public:
void InvalidateMemory(VAddr addr, u64 size) const; void InvalidateMemory(VAddr addr, u64 size) const;
int IsStack(VAddr addr, void** start, void** end);
private: private:
VMAHandle FindVMA(VAddr target) { VMAHandle FindVMA(VAddr target) {
return std::prev(vma_map.upper_bound(target)); return std::prev(vma_map.upper_bound(target));

View File

@ -3,10 +3,9 @@
#pragma once #pragma once
#include <cstring>
#include "common/types.h" #include "common/types.h"
void* memset(void* ptr, int value, size_t num);
namespace Xbyak { namespace Xbyak {
class CodeGenerator; class CodeGenerator;
} }

View File

@ -60,7 +60,7 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
float angle = atan2(d_y, d_x); float angle = atan2(d_y, d_x);
float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed; float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed;
if (d_x != 0 && d_y != 0) { if (d_x != 0 || d_y != 0) {
controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, a_x)); controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, a_x));
controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, a_y)); controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, a_y));
} else { } else {

View File

@ -53,7 +53,7 @@ void Translator::S_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1)); ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1));
IR::ScalarReg dst_reg{inst.dst[0].code}; IR::ScalarReg dst_reg{inst.dst[0].code};
for (u32 i = 0; i < num_dwords; i++) { for (u32 i = 0; i < num_dwords; i++) {
ir.SetScalarReg(dst_reg++, ir.ReadConst(base, ir.Imm32(dword_offset + i))); ir.SetScalarReg(dst_reg + i, ir.ReadConst(base, ir.Imm32(dword_offset + i)));
} }
} }
@ -75,7 +75,7 @@ void Translator::S_BUFFER_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
IR::ScalarReg dst_reg{inst.dst[0].code}; IR::ScalarReg dst_reg{inst.dst[0].code};
for (u32 i = 0; i < num_dwords; i++) { for (u32 i = 0; i < num_dwords; i++) {
const IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i)); const IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i));
ir.SetScalarReg(dst_reg++, ir.ReadConstBuffer(vsharp, index)); ir.SetScalarReg(dst_reg + i, ir.ReadConstBuffer(vsharp, index));
} }
} }

View File

@ -989,13 +989,22 @@ void Translator::V_CMP_NE_U64(const GcnInst& inst) {
} }
}; };
const IR::U1 src0{get_src(inst.src[0])}; const IR::U1 src0{get_src(inst.src[0])};
ASSERT(inst.src[1].field == OperandField::ConstZero); // src0 != 0 auto op = [&inst, this](auto x) {
switch (inst.src[1].field) {
case OperandField::ConstZero:
return x;
case OperandField::SignedConstIntNeg:
return ir.LogicalNot(x);
default:
UNREACHABLE_MSG("unhandled V_CMP_NE_U64 source argument {}", u32(inst.src[1].field));
}
};
switch (inst.dst[1].field) { switch (inst.dst[1].field) {
case OperandField::VccLo: case OperandField::VccLo:
ir.SetVcc(src0); ir.SetVcc(op(src0));
break; break;
case OperandField::ScalarGPR: case OperandField::ScalarGPR:
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), src0); ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), op(src0));
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();

View File

@ -62,7 +62,14 @@ struct BufferResource {
} }
bool IsStorage(const AmdGpu::Buffer& buffer, const Profile& profile) const noexcept { bool IsStorage(const AmdGpu::Buffer& buffer, const Profile& profile) const noexcept {
return buffer.GetSize() > profile.max_ubo_size || is_written; // When using uniform buffers, a size is required at compilation time, so we need to
// either compile a lot of shader specializations to handle each size or just force it to
// the maximum possible size always. However, for some vendors the shader-supplied size is
// used for bounds checking uniform buffer accesses, so the latter would effectively turn
// off buffer robustness behavior. Instead, force storage buffers which are bounds checked
// using the actual buffer size. We are assuming the performance hit from this is
// acceptable.
return true; // buffer.GetSize() > profile.max_ubo_size || is_written;
} }
[[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; [[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept;

View File

@ -584,7 +584,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
break; break;
} }
case PM4ItOpcode::EventWrite: { case PM4ItOpcode::EventWrite: {
// const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header); const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header);
LOG_DEBUG(Render_Vulkan,
"Encountered EventWrite: event_type = {}, event_index = {}",
magic_enum::enum_name(event->event_type.Value()),
magic_enum::enum_name(event->event_index.Value()));
if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) {
// TODO: handle proper synchronization, for now signal that update is done
// immediately
regs.cp_strmout_cntl.offset_update_done = 1;
}
break; break;
} }
case PM4ItOpcode::EventWriteEos: { case PM4ItOpcode::EventWriteEos: {
@ -696,10 +705,10 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
const u64* wait_addr = wait_reg_mem->Address<u64*>(); const u64* wait_addr = wait_reg_mem->Address<u64*>();
if (vo_port->IsVoLabel(wait_addr) && if (vo_port->IsVoLabel(wait_addr) &&
num_submits == mapped_queues[GfxQueueId].submits.size()) { num_submits == mapped_queues[GfxQueueId].submits.size()) {
vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(); }); vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(regs.reg_array); });
break; break;
} }
while (!wait_reg_mem->Test()) { while (!wait_reg_mem->Test(regs.reg_array)) {
YIELD_GFX(); YIELD_GFX();
} }
break; break;
@ -732,6 +741,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
} }
break; break;
} }
case PM4ItOpcode::StrmoutBufferUpdate: {
const auto* strmout = reinterpret_cast<const PM4CmdStrmoutBufferUpdate*>(header);
LOG_WARNING(Render_Vulkan,
"Unimplemented IT_STRMOUT_BUFFER_UPDATE, update_memory = {}, "
"source_select = {}, buffer_select = {}",
strmout->update_memory.Value(),
magic_enum::enum_name(strmout->source_select.Value()),
strmout->buffer_select.Value());
break;
}
default: default:
UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}",
static_cast<u32>(opcode), count); static_cast<u32>(opcode), count);
@ -866,8 +885,9 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq
} }
case PM4ItOpcode::SetQueueReg: { case PM4ItOpcode::SetQueueReg: {
const auto* set_data = reinterpret_cast<const PM4CmdSetQueueReg*>(header); const auto* set_data = reinterpret_cast<const PM4CmdSetQueueReg*>(header);
UNREACHABLE_MSG("Encountered compute SetQueueReg: vqid = {}, reg_offset = {:#x}", LOG_WARNING(Render, "Encountered compute SetQueueReg: vqid = {}, reg_offset = {:#x}",
set_data->vqid.Value(), set_data->reg_offset.Value()); set_data->vqid.Value(), set_data->reg_offset.Value());
break;
} }
case PM4ItOpcode::DispatchDirect: { case PM4ItOpcode::DispatchDirect: {
const auto* dispatch_direct = reinterpret_cast<const PM4CmdDispatchDirect*>(header); const auto* dispatch_direct = reinterpret_cast<const PM4CmdDispatchDirect*>(header);
@ -934,7 +954,7 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq
case PM4ItOpcode::WaitRegMem: { case PM4ItOpcode::WaitRegMem: {
const auto* wait_reg_mem = reinterpret_cast<const PM4CmdWaitRegMem*>(header); const auto* wait_reg_mem = reinterpret_cast<const PM4CmdWaitRegMem*>(header);
ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me);
while (!wait_reg_mem->Test()) { while (!wait_reg_mem->Test(regs.reg_array)) {
YIELD_ASC(vqid); YIELD_ASC(vqid);
} }
break; break;

View File

@ -1175,6 +1175,14 @@ struct Liverpool {
BitField<22, 2, u32> onchip; BitField<22, 2, u32> onchip;
}; };
union StreamOutControl {
u32 raw;
struct {
u32 offset_update_done : 1;
u32 : 31;
};
};
union StreamOutConfig { union StreamOutConfig {
u32 raw; u32 raw;
struct { struct {
@ -1378,7 +1386,9 @@ struct Liverpool {
AaConfig aa_config; AaConfig aa_config;
INSERT_PADDING_WORDS(0xA318 - 0xA2F8 - 1); INSERT_PADDING_WORDS(0xA318 - 0xA2F8 - 1);
ColorBuffer color_buffers[NumColorBuffers]; ColorBuffer color_buffers[NumColorBuffers];
INSERT_PADDING_WORDS(0xC242 - 0xA390); INSERT_PADDING_WORDS(0xC03F - 0xA390);
StreamOutControl cp_strmout_cntl;
INSERT_PADDING_WORDS(0xC242 - 0xC040);
PrimitiveType primitive_type; PrimitiveType primitive_type;
INSERT_PADDING_WORDS(0xC24C - 0xC243); INSERT_PADDING_WORDS(0xC24C - 0xC243);
u32 num_indices; u32 num_indices;
@ -1668,6 +1678,7 @@ static_assert(GFX6_3D_REG_INDEX(color_buffers[0].base_address) == 0xA318);
static_assert(GFX6_3D_REG_INDEX(color_buffers[0].pitch) == 0xA319); static_assert(GFX6_3D_REG_INDEX(color_buffers[0].pitch) == 0xA319);
static_assert(GFX6_3D_REG_INDEX(color_buffers[0].slice) == 0xA31A); static_assert(GFX6_3D_REG_INDEX(color_buffers[0].slice) == 0xA31A);
static_assert(GFX6_3D_REG_INDEX(color_buffers[7].base_address) == 0xA381); static_assert(GFX6_3D_REG_INDEX(color_buffers[7].base_address) == 0xA381);
static_assert(GFX6_3D_REG_INDEX(cp_strmout_cntl) == 0xC03F);
static_assert(GFX6_3D_REG_INDEX(primitive_type) == 0xC242); static_assert(GFX6_3D_REG_INDEX(primitive_type) == 0xC242);
static_assert(GFX6_3D_REG_INDEX(num_instances) == 0xC24D); static_assert(GFX6_3D_REG_INDEX(num_instances) == 0xC24D);
static_assert(GFX6_3D_REG_INDEX(vgt_tf_memory_base) == 0xc250); static_assert(GFX6_3D_REG_INDEX(vgt_tf_memory_base) == 0xc250);

View File

@ -246,6 +246,46 @@ struct PM4CmdNop {
}; };
}; };
enum class SourceSelect : u32 {
BufferOffset = 0,
VgtStrmoutBufferFilledSize = 1,
SrcAddress = 2,
None = 3,
};
struct PM4CmdStrmoutBufferUpdate {
PM4Type3Header header;
union {
BitField<0, 1, u32> update_memory;
BitField<1, 2, SourceSelect> source_select;
BitField<8, 2, u32> buffer_select;
u32 control;
};
union {
BitField<2, 30, u32> dst_address_lo;
BitField<0, 2, u32> swap_dst;
};
u32 dst_address_hi;
union {
u32 buffer_offset;
BitField<2, 30, u32> src_address_lo;
BitField<0, 2, u32> swap_src;
};
u32 src_address_hi;
template <typename T = u64>
T DstAddress() const {
ASSERT(update_memory.Value() == 1);
return reinterpret_cast<T>(dst_address_lo.Value() | u64(dst_address_hi & 0xFFFF) << 32);
}
template <typename T = u64>
T SrcAddress() const {
ASSERT(source_select.Value() == SourceSelect::SrcAddress);
return reinterpret_cast<T>(src_address_lo.Value() | u64(src_address_hi & 0xFFFF) << 32);
}
};
struct PM4CmdDrawIndexOffset2 { struct PM4CmdDrawIndexOffset2 {
PM4Type3Header header; PM4Type3Header header;
u32 max_size; ///< Maximum number of indices u32 max_size; ///< Maximum number of indices
@ -303,6 +343,80 @@ static u64 GetGpuClock64() {
return static_cast<u64>(ticks); return static_cast<u64>(ticks);
} }
// VGT_EVENT_INITIATOR.EVENT_TYPE
enum class EventType : u32 {
SampleStreamoutStats1 = 1,
SampleStreamoutStats2 = 2,
SampleStreamoutStats3 = 3,
CacheFlushTs = 4,
ContextDone = 5,
CacheFlush = 6,
CsPartialFlush = 7,
VgtStreamoutSync = 8,
VgtStreamoutReset = 10,
EndOfPipeIncrDe = 11,
EndOfPipeIbEnd = 12,
RstPixCnt = 13,
VsPartialFlush = 15,
PsPartialFlush = 16,
FlushHsOutput = 17,
FlushLsOutput = 18,
CacheFlushAndInvTsEvent = 20,
ZpassDone = 21,
CacheFlushAndInvEvent = 22,
PerfcounterStart = 23,
PerfcounterStop = 24,
PipelineStatStart = 25,
PipelineStatStop = 26,
PerfcounterSample = 27,
FlushEsOutput = 28,
FlushGsOutput = 29,
SamplePipelineStat = 30,
SoVgtStreamoutFlush = 31,
SampleStreamoutStats = 32,
ResetVtxCnt = 33,
VgtFlush = 36,
ScSendDbVpz = 39,
BottomOfPipeTs = 40,
DbCacheFlushAndInv = 42,
FlushAndInvDbDataTs = 43,
FlushAndInvDbMeta = 44,
FlushAndInvCbDataTs = 45,
FlushAndInvCbMeta = 46,
CsDone = 47,
PsDone = 48,
FlushAndInvCbPixelData = 49,
ThreadTraceStart = 51,
ThreadTraceStop = 52,
ThreadTraceFlush = 54,
ThreadTraceFinish = 55,
PixelPipeStatControl = 56,
PixelPipeStatDump = 57,
PixelPipeStatReset = 58,
};
enum class EventIndex : u32 {
Other = 0,
ZpassDone = 1,
SamplePipelineStat = 2,
SampleStreamoutStatSx = 3,
CsVsPsPartialFlush = 4,
EopReserved = 5,
EosReserved = 6,
CacheFlush = 7,
};
struct PM4CmdEventWrite {
PM4Type3Header header;
union {
u32 event_control;
BitField<0, 6, EventType> event_type; ///< Event type written to VGT_EVENT_INITIATOR
BitField<8, 4, EventIndex> event_index; ///< Event index
BitField<20, 1, u32> inv_l2; ///< Send WBINVL2 op to the TC L2 cache when EVENT_INDEX = 0111
};
u32 address[];
};
struct PM4CmdEventWriteEop { struct PM4CmdEventWriteEop {
PM4Type3Header header; PM4Type3Header header;
union { union {
@ -474,7 +588,12 @@ struct PM4CmdWaitRegMem {
BitField<8, 1, Engine> engine; BitField<8, 1, Engine> engine;
u32 raw; u32 raw;
}; };
u32 poll_addr_lo; union {
BitField<0, 16, u32> reg;
BitField<2, 30, u32> poll_addr_lo;
BitField<0, 2, u32> swap;
u32 poll_addr_lo_raw;
};
u32 poll_addr_hi; u32 poll_addr_hi;
u32 ref; u32 ref;
u32 mask; u32 mask;
@ -482,31 +601,36 @@ struct PM4CmdWaitRegMem {
template <typename T = u32*> template <typename T = u32*>
T Address() const { T Address() const {
return std::bit_cast<T>((uintptr_t(poll_addr_hi) << 32) | poll_addr_lo); return std::bit_cast<T>((uintptr_t(poll_addr_hi) << 32) | (poll_addr_lo << 2));
} }
bool Test() const { u32 Reg() const {
return reg.Value();
}
bool Test(const std::array<u32, Liverpool::NumRegs>& regs) const {
u32 value = mem_space.Value() == MemSpace::Memory ? *Address() : regs[Reg()];
switch (function.Value()) { switch (function.Value()) {
case Function::Always: { case Function::Always: {
return true; return true;
} }
case Function::LessThan: { case Function::LessThan: {
return (*Address() & mask) < ref; return (value & mask) < ref;
} }
case Function::LessThanEqual: { case Function::LessThanEqual: {
return (*Address() & mask) <= ref; return (value & mask) <= ref;
} }
case Function::Equal: { case Function::Equal: {
return (*Address() & mask) == ref; return (value & mask) == ref;
} }
case Function::NotEqual: { case Function::NotEqual: {
return (*Address() & mask) != ref; return (value & mask) != ref;
} }
case Function::GreaterThanEqual: { case Function::GreaterThanEqual: {
return (*Address() & mask) >= ref; return (value & mask) >= ref;
} }
case Function::GreaterThan: { case Function::GreaterThan: {
return (*Address() & mask) > ref; return (value & mask) > ref;
} }
case Function::Reserved: case Function::Reserved:
[[fallthrough]]; [[fallthrough]];

View File

@ -238,7 +238,15 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo
.bufferMemoryBarrierCount = 1, .bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &pre_barrier, .pBufferMemoryBarriers = &pre_barrier,
}); });
cmdbuf.updateBuffer(buffer->Handle(), buffer->Offset(address), num_bytes, value); // vkCmdUpdateBuffer can only copy up to 65536 bytes at a time.
static constexpr u32 UpdateBufferMaxSize = 65536;
const auto dst_offset = buffer->Offset(address);
for (u32 offset = 0; offset < num_bytes; offset += UpdateBufferMaxSize) {
const auto* update_src = static_cast<const u8*>(value) + offset;
const auto update_dst = dst_offset + offset;
const auto update_size = std::min(num_bytes - offset, UpdateBufferMaxSize);
cmdbuf.updateBuffer(buffer->Handle(), update_dst, update_size, update_src);
}
cmdbuf.pipelineBarrier2(vk::DependencyInfo{ cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion, .dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1, .bufferMemoryBarrierCount = 1,

View File

@ -261,6 +261,8 @@ bool Instance::CreateDevice() {
robustness2_features = feature_chain.get<vk::PhysicalDeviceRobustness2FeaturesEXT>(); robustness2_features = feature_chain.get<vk::PhysicalDeviceRobustness2FeaturesEXT>();
LOG_INFO(Render_Vulkan, "- robustBufferAccess2: {}", LOG_INFO(Render_Vulkan, "- robustBufferAccess2: {}",
robustness2_features.robustBufferAccess2); robustness2_features.robustBufferAccess2);
LOG_INFO(Render_Vulkan, "- robustImageAccess2: {}",
robustness2_features.robustImageAccess2);
LOG_INFO(Render_Vulkan, "- nullDescriptor: {}", robustness2_features.nullDescriptor); LOG_INFO(Render_Vulkan, "- nullDescriptor: {}", robustness2_features.nullDescriptor);
} }
custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
@ -337,6 +339,7 @@ bool Instance::CreateDevice() {
.independentBlend = features.independentBlend, .independentBlend = features.independentBlend,
.geometryShader = features.geometryShader, .geometryShader = features.geometryShader,
.tessellationShader = features.tessellationShader, .tessellationShader = features.tessellationShader,
.dualSrcBlend = features.dualSrcBlend,
.logicOp = features.logicOp, .logicOp = features.logicOp,
.multiDrawIndirect = features.multiDrawIndirect, .multiDrawIndirect = features.multiDrawIndirect,
.depthBiasClamp = features.depthBiasClamp, .depthBiasClamp = features.depthBiasClamp,
@ -394,6 +397,7 @@ bool Instance::CreateDevice() {
}, },
vk::PhysicalDeviceRobustness2FeaturesEXT{ vk::PhysicalDeviceRobustness2FeaturesEXT{
.robustBufferAccess2 = robustness2_features.robustBufferAccess2, .robustBufferAccess2 = robustness2_features.robustBufferAccess2,
.robustImageAccess2 = robustness2_features.robustImageAccess2,
.nullDescriptor = robustness2_features.nullDescriptor, .nullDescriptor = robustness2_features.nullDescriptor,
}, },
vk::PhysicalDeviceVertexInputDynamicStateFeaturesEXT{ vk::PhysicalDeviceVertexInputDynamicStateFeaturesEXT{

View File

@ -205,7 +205,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
.supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(),
.supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(), .supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(),
.supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(), .supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(),
.supports_robust_buffer_access = instance_.IsRobustBufferAccess2Supported(), // TODO: Emitted bounds checks cause problems with phi control flow; needs to be fixed.
.supports_robust_buffer_access = true, // instance_.IsRobustBufferAccess2Supported(),
.supports_image_fp32_atomic_min_max = instance_.IsShaderAtomicFloatImage32MinMaxSupported(), .supports_image_fp32_atomic_min_max = instance_.IsShaderAtomicFloatImage32MinMaxSupported(),
.needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() &&
instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, instance.GetDriverID() == vk::DriverId::eNvidiaProprietary,