From ab36a238bff21bec5dc6534e28a1e0b97921d04b Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 22 Jun 2024 18:59:06 +0200 Subject: [PATCH] kernel: event_queue: `sceKernelAddHRTimerEvent` implementation --- src/common/debug.h | 2 ++ src/core/libraries/kernel/event_queue.cpp | 22 +++++++++----- src/core/libraries/kernel/event_queue.h | 35 ++++++++++++++++++---- src/core/libraries/kernel/event_queues.cpp | 29 ++++++++++++++++-- src/core/libraries/kernel/event_queues.h | 1 + src/core/libraries/kernel/libkernel.cpp | 1 + src/core/libraries/videoout/video_out.cpp | 2 -- 7 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/common/debug.h b/src/common/debug.h index f9974228..50022a15 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -41,6 +41,8 @@ enum MarkersPallete : int { #define RENDERER_TRACE ZoneScopedC(RendererMarkerColor) #define HLE_TRACE ZoneScopedC(HleMarkerColor) +#define TRACE_HINT(str) ZoneText(str.c_str(), str.size()) + #define TRACE_WARN(msg) \ [](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg); #define TRACE_ERROR(msg) \ diff --git a/src/core/libraries/kernel/event_queue.cpp b/src/core/libraries/kernel/event_queue.cpp index 6392d078..2a0cb3ef 100644 --- a/src/core/libraries/kernel/event_queue.cpp +++ b/src/core/libraries/kernel/event_queue.cpp @@ -8,13 +8,19 @@ namespace Libraries::Kernel { EqueueInternal::~EqueueInternal() = default; -int EqueueInternal::addEvent(const EqueueEvent& event) { +int EqueueInternal::addEvent(EqueueEvent& event) { std::scoped_lock lock{m_mutex}; - ASSERT(!event.isTriggered); + const auto start_clock = std::chrono::high_resolution_clock::now(); + event.filter.added_time_us = + std::chrono::time_point_cast(start_clock); - // TODO check if event is already exists and return it. Currently we just add in m_events array - m_events.push_back(event); + const auto& it = std::ranges::find(m_events, event); + if (it != m_events.cend()) { + *it = event; + } else { + m_events.push_back(event); + } return 0; } @@ -50,8 +56,8 @@ bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) { std::scoped_lock lock{m_mutex}; for (auto& event : m_events) { - if (event.event.ident == ident) { // event filter? - event.trigger(trigger_data); + if (event.event.ident == ident && event.event.filter == filter) { + event.Trigger(trigger_data); } } } @@ -64,9 +70,9 @@ int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) { int ret = 0; for (auto& event : m_events) { - if (event.isTriggered) { + if (event.IsTriggered()) { ev[ret++] = event.event; - event.reset(); + event.Reset(); } } diff --git a/src/core/libraries/kernel/event_queue.h b/src/core/libraries/kernel/event_queue.h index 8fc5f5d3..25f4c453 100644 --- a/src/core/libraries/kernel/event_queue.h +++ b/src/core/libraries/kernel/event_queue.h @@ -70,24 +70,44 @@ struct SceKernelEvent { struct Filter { void* data = nullptr; + std::chrono::time_point added_time_us; }; struct EqueueEvent { - bool isTriggered = false; SceKernelEvent event; Filter filter; - void reset() { - isTriggered = false; + void Reset() { + is_triggered = false; event.fflags = 0; event.data = 0; } - void trigger(void* data) { - isTriggered = true; + void Trigger(void* data) { + is_triggered = true; event.fflags++; event.data = reinterpret_cast(data); } + + bool IsTriggered() const { + if (event.filter == Kernel::EVFILT_HRTIMER) { + // For HR timers we don't want to waste time on spawning waiters. Instead, we will check + // for time out condition every time caller fetches the event status. + const auto now_clock = std::chrono::high_resolution_clock::now(); + const auto now_time_us = + std::chrono::time_point_cast(now_clock); + const auto delta = (now_time_us - filter.added_time_us).count(); + return (s64(event.data) - delta) <= 0; + } + return is_triggered; + } + + bool operator==(const EqueueEvent& ev) const { + return ev.event.ident == event.ident; + } + +private: + bool is_triggered = false; }; class EqueueInternal { @@ -97,7 +117,10 @@ public: void setName(const std::string& m_name) { this->m_name = m_name; } - int addEvent(const EqueueEvent& event); + const auto& GetName() const { + return m_name; + } + int addEvent(EqueueEvent& event); int removeEvent(u64 id); int waitForEvents(SceKernelEvent* ev, int num, u32 micros); bool triggerEvent(u64 ident, s16 filter, void* trigger_data); diff --git a/src/core/libraries/kernel/event_queues.cpp b/src/core/libraries/kernel/event_queues.cpp index e2b151a8..e93c77fa 100644 --- a/src/core/libraries/kernel/event_queues.cpp +++ b/src/core/libraries/kernel/event_queues.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "common/debug.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/event_queues.h" @@ -43,7 +44,9 @@ int PS4_SYSV_ABI sceKernelDeleteEqueue(SceKernelEqueue eq) { int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out, SceKernelUseconds* timo) { - LOG_INFO(Kernel_Event, "num = {}", num); + HLE_TRACE; + TRACE_HINT(eq->GetName()); + LOG_INFO(Kernel_Event, "equeue = {} num = {}", eq->GetName(), num); if (eq == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; @@ -84,7 +87,6 @@ int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) { } Kernel::EqueueEvent event{}; - event.isTriggered = false; event.event.ident = id; event.event.filter = Kernel::EVFILT_USER; event.event.udata = 0; @@ -101,7 +103,6 @@ int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) { } Kernel::EqueueEvent event{}; - event.isTriggered = false; event.event.ident = id; event.event.filter = Kernel::EVFILT_USER; event.event.udata = 0; @@ -112,6 +113,28 @@ int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) { return eq->addEvent(event); } +s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) { + if (eq == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (ts->tv_sec > 100 || ts->tv_nsec < 100'000) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + ASSERT(ts->tv_nsec > 1000); // assume 1us granularity + const auto total_us = ts->tv_sec * 1000'000 + ts->tv_nsec / 1000; + + Kernel::EqueueEvent event{}; + event.event.ident = id; + event.event.filter = Kernel::EVFILT_HRTIMER; + event.event.flags = 0x11; + event.event.fflags = 0; + event.event.data = total_us; + event.event.udata = udata; + + return eq->addEvent(event); +} + void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev) { if (!ev) { return nullptr; diff --git a/src/core/libraries/kernel/event_queues.h b/src/core/libraries/kernel/event_queues.h index 8c1521b7..2549203e 100644 --- a/src/core/libraries/kernel/event_queues.h +++ b/src/core/libraries/kernel/event_queues.h @@ -19,5 +19,6 @@ int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* uda int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id); int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id); int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id); +s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 15b290f7..ec97ecd7 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -328,6 +328,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData); LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); + LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 0c40f1c4..6fa081e1 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -48,7 +48,6 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, } Kernel::EqueueEvent event{}; - event.isTriggered = false; event.event.ident = SCE_VIDEO_OUT_EVENT_FLIP; event.event.filter = Kernel::EVFILT_VIDEO_OUT; event.event.udata = udata; @@ -73,7 +72,6 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl } Kernel::EqueueEvent event{}; - event.isTriggered = false; event.event.ident = SCE_VIDEO_OUT_EVENT_VBLANK; event.event.filter = Kernel::EVFILT_VIDEO_OUT; event.event.udata = udata;