kernel: event_queue: `sceKernelAddHRTimerEvent` implementation

This commit is contained in:
psucien 2024-06-22 18:59:06 +02:00
parent cb6b21de1f
commit ab36a238bf
7 changed files with 73 additions and 19 deletions

View File

@ -41,6 +41,8 @@ enum MarkersPallete : int {
#define RENDERER_TRACE ZoneScopedC(RendererMarkerColor) #define RENDERER_TRACE ZoneScopedC(RendererMarkerColor)
#define HLE_TRACE ZoneScopedC(HleMarkerColor) #define HLE_TRACE ZoneScopedC(HleMarkerColor)
#define TRACE_HINT(str) ZoneText(str.c_str(), str.size())
#define TRACE_WARN(msg) \ #define TRACE_WARN(msg) \
[](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg); [](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg);
#define TRACE_ERROR(msg) \ #define TRACE_ERROR(msg) \

View File

@ -8,13 +8,19 @@ namespace Libraries::Kernel {
EqueueInternal::~EqueueInternal() = default; EqueueInternal::~EqueueInternal() = default;
int EqueueInternal::addEvent(const EqueueEvent& event) { int EqueueInternal::addEvent(EqueueEvent& event) {
std::scoped_lock lock{m_mutex}; 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<std::chrono::microseconds>(start_clock);
// TODO check if event is already exists and return it. Currently we just add in m_events array const auto& it = std::ranges::find(m_events, event);
m_events.push_back(event); if (it != m_events.cend()) {
*it = event;
} else {
m_events.push_back(event);
}
return 0; return 0;
} }
@ -50,8 +56,8 @@ bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
for (auto& event : m_events) { for (auto& event : m_events) {
if (event.event.ident == ident) { // event filter? if (event.event.ident == ident && event.event.filter == filter) {
event.trigger(trigger_data); event.Trigger(trigger_data);
} }
} }
} }
@ -64,9 +70,9 @@ int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
int ret = 0; int ret = 0;
for (auto& event : m_events) { for (auto& event : m_events) {
if (event.isTriggered) { if (event.IsTriggered()) {
ev[ret++] = event.event; ev[ret++] = event.event;
event.reset(); event.Reset();
} }
} }

View File

@ -70,24 +70,44 @@ struct SceKernelEvent {
struct Filter { struct Filter {
void* data = nullptr; void* data = nullptr;
std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds> added_time_us;
}; };
struct EqueueEvent { struct EqueueEvent {
bool isTriggered = false;
SceKernelEvent event; SceKernelEvent event;
Filter filter; Filter filter;
void reset() { void Reset() {
isTriggered = false; is_triggered = false;
event.fflags = 0; event.fflags = 0;
event.data = 0; event.data = 0;
} }
void trigger(void* data) { void Trigger(void* data) {
isTriggered = true; is_triggered = true;
event.fflags++; event.fflags++;
event.data = reinterpret_cast<uintptr_t>(data); event.data = reinterpret_cast<uintptr_t>(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<std::chrono::microseconds>(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 { class EqueueInternal {
@ -97,7 +117,10 @@ public:
void setName(const std::string& m_name) { void setName(const std::string& m_name) {
this->m_name = 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 removeEvent(u64 id);
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);

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h" #include "common/assert.h"
#include "common/debug.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/kernel/event_queues.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, int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out,
SceKernelUseconds* timo) { 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) { if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
@ -84,7 +87,6 @@ int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
} }
Kernel::EqueueEvent event{}; Kernel::EqueueEvent event{};
event.isTriggered = false;
event.event.ident = id; event.event.ident = id;
event.event.filter = Kernel::EVFILT_USER; event.event.filter = Kernel::EVFILT_USER;
event.event.udata = 0; event.event.udata = 0;
@ -101,7 +103,6 @@ int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) {
} }
Kernel::EqueueEvent event{}; Kernel::EqueueEvent event{};
event.isTriggered = false;
event.event.ident = id; event.event.ident = id;
event.event.filter = Kernel::EVFILT_USER; event.event.filter = Kernel::EVFILT_USER;
event.event.udata = 0; event.event.udata = 0;
@ -112,6 +113,28 @@ int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) {
return eq->addEvent(event); 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) { void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev) {
if (!ev) { if (!ev) {
return nullptr; return nullptr;

View File

@ -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 sceKernelDeleteUserEvent(SceKernelEqueue eq, int id);
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id); int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id);
int PS4_SYSV_ABI sceKernelAddUserEventEdge(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 } // namespace Libraries::Kernel

View File

@ -328,6 +328,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData); LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData);
LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent);
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("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);

View File

@ -48,7 +48,6 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle,
} }
Kernel::EqueueEvent event{}; Kernel::EqueueEvent event{};
event.isTriggered = false;
event.event.ident = SCE_VIDEO_OUT_EVENT_FLIP; event.event.ident = SCE_VIDEO_OUT_EVENT_FLIP;
event.event.filter = Kernel::EVFILT_VIDEO_OUT; event.event.filter = Kernel::EVFILT_VIDEO_OUT;
event.event.udata = udata; event.event.udata = udata;
@ -73,7 +72,6 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl
} }
Kernel::EqueueEvent event{}; Kernel::EqueueEvent event{};
event.isTriggered = false;
event.event.ident = SCE_VIDEO_OUT_EVENT_VBLANK; event.event.ident = SCE_VIDEO_OUT_EVENT_VBLANK;
event.event.filter = Kernel::EVFILT_VIDEO_OUT; event.event.filter = Kernel::EVFILT_VIDEO_OUT;
event.event.udata = udata; event.event.udata = udata;