mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-12 20:55:56 +00:00
Merge branch 'main' into fontlib
This commit is contained in:
commit
9569c29644
24 changed files with 727 additions and 327 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -1162,7 +1155,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:
|
||||||
|
|
|
@ -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
|
||||||
|
@ -55,4 +55,4 @@ To configure the emulator, you can go through the interface and go to "settings"
|
||||||
|
|
||||||
You can also configure the emulator by editing the `config.toml` file located in the `user` folder created after the application is started (Mostly useful if you are using the SDL version).
|
You can also configure the emulator by editing the `config.toml` file located in the `user` folder created after the application is started (Mostly useful if you are using the SDL version).
|
||||||
Some settings may be related to more technical development and debugging.\
|
Some settings may be related to more technical development and debugging.\
|
||||||
For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
|
For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
|
||||||
|
|
6
externals/CMakeLists.txt
vendored
6
externals/CMakeLists.txt
vendored
|
@ -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
externals/winpthreads
vendored
1
externals/winpthreads
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f
|
|
|
@ -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, ¶ms);
|
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -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{};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -108,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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
SetPosixErrno(EINTR);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (total_wait_time.count() > 0) {
|
s32 PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
||||||
auto wait_time = std::chrono::ceil<std::chrono::milliseconds>(total_wait_time).count();
|
return posix_nanosleep_impl(rqtp, rmtp, true);
|
||||||
u64 res = SleepEx(static_cast<u64>(wait_time), true);
|
}
|
||||||
if (res == WAIT_IO_COMPLETION) {
|
|
||||||
auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time;
|
s32 PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
||||||
auto elapsedMicroseconds =
|
if (const auto ret = posix_nanosleep_impl(rqtp, rmtp, false); ret < 0) {
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(elapsedTime).count();
|
return ErrnoToSceKernelError(*__Error());
|
||||||
total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds);
|
}
|
||||||
} else {
|
return ORBIS_OK;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (clock_id == ORBIS_CLOCK_PROCTIME) {
|
||||||
#else
|
const auto us = sceKernelGetProcessTime();
|
||||||
timespec start;
|
ts->tv_sec = static_cast<s64>(us / 1'000'000);
|
||||||
timespec remain;
|
ts->tv_nsec = static_cast<s64>((us % 1'000'000) * 1000);
|
||||||
start.tv_sec = microseconds / 1000000;
|
return 0;
|
||||||
start.tv_nsec = (microseconds % 1000000) * 1000;
|
}
|
||||||
timespec* requested = &start;
|
if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
|
||||||
int ret = 0;
|
clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
|
||||||
do {
|
LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
|
||||||
ret = nanosleep(requested, &remain);
|
clock_id = ORBIS_CLOCK_MONOTONIC;
|
||||||
requested = &remain;
|
}
|
||||||
} while (ret != 0);
|
|
||||||
return ret;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_usleep(u32 microseconds) {
|
#ifdef _WIN32
|
||||||
return sceKernelUsleep(microseconds);
|
static const auto FileTimeTo100Ns = [](FILETIME& ft) { return *reinterpret_cast<u64*>(&ft); };
|
||||||
}
|
|
||||||
|
|
||||||
u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(seconds));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN64
|
|
||||||
#ifndef CLOCK_REALTIME
|
|
||||||
#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) {
|
|
||||||
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,196 +245,226 @@ 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;
|
||||||
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) {
|
|
||||||
const auto res = orbis_clock_gettime(clock_id, tp);
|
|
||||||
if (res < 0) {
|
|
||||||
return ErrnoToSceKernelError(res);
|
|
||||||
}
|
}
|
||||||
return ORBIS_OK;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
|
||||||
const auto* request = reinterpret_cast<const timespec*>(rqtp);
|
|
||||||
auto* remain = reinterpret_cast<timespec*>(rmtp);
|
|
||||||
return nanosleep(request, remain);
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
|
||||||
if (!rqtp || !rmtp) {
|
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) {
|
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return posix_nanosleep(rqtp, rmtp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
|
|
||||||
if (!tp) {
|
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN64
|
|
||||||
FILETIME filetime;
|
|
||||||
GetSystemTimePreciseAsFileTime(&filetime);
|
|
||||||
|
|
||||||
constexpr u64 UNIX_TIME_START = 0x295E9648864000;
|
|
||||||
constexpr u64 TICKS_PER_SECOND = 1000000;
|
|
||||||
|
|
||||||
u64 ticks = filetime.dwHighDateTime;
|
|
||||||
ticks <<= 32;
|
|
||||||
ticks |= filetime.dwLowDateTime;
|
|
||||||
ticks /= 10;
|
|
||||||
ticks -= UNIX_TIME_START;
|
|
||||||
|
|
||||||
tp->tv_sec = 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
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelClockGettime(const u32 clock_id, OrbisKernelTimespec* ts) {
|
||||||
|
if (const auto ret = posix_clock_gettime(clock_id, ts); ret < 0) {
|
||||||
|
return ErrnoToSceKernelError(*__Error());
|
||||||
|
}
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
|
s32 PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
|
||||||
// FreeBSD docs mention that the kernel generally does not track these values
|
|
||||||
// and they are usually returned as zero.
|
|
||||||
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;
|
|
||||||
if (!tzflag) {
|
|
||||||
_tzset();
|
|
||||||
tzflag++;
|
|
||||||
}
|
|
||||||
tz->tz_minuteswest = _timezone / 60;
|
|
||||||
tz->tz_dsttime = _daylight;
|
|
||||||
#else
|
|
||||||
struct timezone tzz;
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, &tzz);
|
|
||||||
tz->tz_dsttime = tzz.tz_dsttime;
|
|
||||||
tz->tz_minuteswest = tzz.tz_minuteswest;
|
|
||||||
#endif
|
|
||||||
return ORBIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
|
|
||||||
if (res == nullptr) {
|
if (res == nullptr) {
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
clockid_t pclock_id = CLOCK_REALTIME;
|
|
||||||
|
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
|
||||||
|
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) {
|
switch (clock_id) {
|
||||||
case ORBIS_CLOCK_REALTIME:
|
case ORBIS_CLOCK_REALTIME:
|
||||||
case ORBIS_CLOCK_REALTIME_PRECISE:
|
case ORBIS_CLOCK_REALTIME_PRECISE:
|
||||||
case ORBIS_CLOCK_REALTIME_FAST:
|
|
||||||
pclock_id = CLOCK_REALTIME;
|
pclock_id = CLOCK_REALTIME;
|
||||||
break;
|
break;
|
||||||
case ORBIS_CLOCK_SECOND:
|
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:
|
||||||
case ORBIS_CLOCK_MONOTONIC_PRECISE:
|
case ORBIS_CLOCK_MONOTONIC_PRECISE:
|
||||||
case ORBIS_CLOCK_MONOTONIC_FAST:
|
|
||||||
pclock_id = CLOCK_MONOTONIC;
|
pclock_id = CLOCK_MONOTONIC;
|
||||||
break;
|
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:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec t{};
|
timespec t{};
|
||||||
int result = clock_getres(pclock_id, &t);
|
const auto result = clock_getres(pclock_id, &t);
|
||||||
res->tv_sec = t.tv_sec;
|
res->tv_sec = t.tv_sec;
|
||||||
res->tv_nsec = t.tv_nsec;
|
res->tv_nsec = t.tv_nsec;
|
||||||
if (result == 0) {
|
if (result < 0) {
|
||||||
return ORBIS_OK;
|
SetPosixErrno(errno);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
s32 PS4_SYSV_ABI sceKernelClockGetres(const u32 clock_id, OrbisKernelTimespec* res) {
|
||||||
OrbisKernelTimezone* timezone, int* dst_seconds) {
|
if (const auto ret = posix_clock_getres(clock_id, res); ret < 0) {
|
||||||
|
return ErrnoToSceKernelError(*__Error());
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI posix_gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
|
||||||
|
#ifdef _WIN64
|
||||||
|
if (tp) {
|
||||||
|
FILETIME filetime;
|
||||||
|
GetSystemTimePreciseAsFileTime(&filetime);
|
||||||
|
|
||||||
|
constexpr u64 UNIX_TIME_START = 0x295E9648864000;
|
||||||
|
constexpr u64 TICKS_PER_SECOND = 1000000;
|
||||||
|
|
||||||
|
u64 ticks = filetime.dwHighDateTime;
|
||||||
|
ticks <<= 32;
|
||||||
|
ticks |= filetime.dwLowDateTime;
|
||||||
|
ticks /= 10;
|
||||||
|
ticks -= UNIX_TIME_START;
|
||||||
|
|
||||||
|
tp->tv_sec = ticks / TICKS_PER_SECOND;
|
||||||
|
tp->tv_usec = ticks % TICKS_PER_SECOND;
|
||||||
|
}
|
||||||
|
if (tz) {
|
||||||
|
static int tzflag = 0;
|
||||||
|
if (!tzflag) {
|
||||||
|
_tzset();
|
||||||
|
tzflag++;
|
||||||
|
}
|
||||||
|
tz->tz_minuteswest = _timezone / 60;
|
||||||
|
tz->tz_dsttime = _daylight;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
struct timezone tzz;
|
||||||
|
timeval tv;
|
||||||
|
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_minuteswest = tzz.tz_minuteswest;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
SetPosixErrno(errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
|
||||||
|
if (const auto ret = posix_gettimeofday(tp, nullptr); ret < 0) {
|
||||||
|
return ErrnoToSceKernelError(*__Error());
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
|
||||||
|
if (const auto ret = posix_gettimeofday(nullptr, tz); ret < 0) {
|
||||||
|
return ErrnoToSceKernelError(*__Error());
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -949,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
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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]];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue