diff --git a/.gitmodules b/.gitmodules index d6965722..6a73ffa1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,9 +31,6 @@ [submodule "externals/robin-map"] path = externals/robin-map url = https://github.com/Tessil/robin-map.git -[submodule "externals/boost"] - path = externals/boost - url = https://github.com/raphaelthegreat/ext-boost.git [submodule "externals/xbyak"] path = externals/xbyak url = https://github.com/herumi/xbyak.git @@ -58,3 +55,6 @@ [submodule "externals/tracy"] path = externals/tracy url = https://github.com/shadps4-emu/tracy.git +[submodule "externals/ext-boost"] + path = externals/ext-boost + url = https://github.com/shadps4-emu/ext-boost.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c6036ce9..5dda2e81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -576,6 +576,8 @@ if (WIN32) target_sources(shadps4 PRIVATE src/shadps4.rc) endif() +add_definitions(-DBOOST_ASIO_STANDALONE) + target_include_directories(shadps4 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) # Shaders sources diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index ca9c6747..a86c393f 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -12,8 +12,8 @@ endif() # Boost if (NOT TARGET Boost::headers) - set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") - set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") + set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") + set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") add_library(boost INTERFACE) target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR}) diff --git a/externals/boost b/externals/boost deleted file mode 160000 index 87b78171..00000000 --- a/externals/boost +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 87b7817119982e8ad6068855fae31b11590514be diff --git a/externals/ext-boost b/externals/ext-boost new file mode 160000 index 00000000..147b2de7 --- /dev/null +++ b/externals/ext-boost @@ -0,0 +1 @@ +Subproject commit 147b2de7734f5dc3b9aeb1f4135ae15fcd44b9d7 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/common/logging/backend.cpp b/src/common/logging/backend.cpp index 460b7376..0b03c86b 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -170,7 +170,7 @@ public: const char* function, std::string message) { // Propagate important log messages to the profiler if (IsProfilerConnected()) { - const auto& msg_str = std::format("[{}] {}", GetLogClassName(log_class), message); + const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message); switch (log_level) { case Level::Warning: TRACE_WARN(msg_str); diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 653607af..dcf6d99e 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -26,6 +26,17 @@ using namespace AmdGpu; static std::unique_ptr liverpool; +enum GnmEventIdents : u64 { + Compute0RelMem = 0x00, + Compute1RelMem = 0x01, + Compute2RelMem = 0x02, + Compute3RelMem = 0x03, + Compute4RelMem = 0x04, + Compute5RelMem = 0x05, + Compute6RelMem = 0x06, + GfxEop = 0x40 +}; + enum ShaderStages : u32 { Cs, Ps, @@ -327,10 +338,6 @@ static inline u32* ClearContextState(u32* cmdbuf) { s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { LOG_TRACE(Lib_GnmDriver, "called"); - if (id != SceKernelEvent::Type::GfxEop) { - return ORBIS_OK; - } - ASSERT_MSG(id == SceKernelEvent::Type::GfxEop); if (!eq) { return ORBIS_KERNEL_ERROR_EBADF; @@ -338,20 +345,20 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { EqueueEvent kernel_event{}; kernel_event.event.ident = id; - kernel_event.event.filter = EVFILT_GRAPHICS_CORE; - kernel_event.event.flags = 1; + kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore; + kernel_event.event.flags = SceKernelEvent::Flags::Add; kernel_event.event.fflags = 0; kernel_event.event.data = id; kernel_event.event.udata = udata; - eq->addEvent(kernel_event); + eq->AddEvent(kernel_event); Platform::IrqC::Instance()->Register( Platform::InterruptId::GfxEop, [=](Platform::InterruptId irq) { ASSERT_MSG(irq == Platform::InterruptId::GfxEop, - "An unexpected IRQ occured"); // We need to conver IRQ# to event id and do + "An unexpected IRQ occured"); // We need to convert IRQ# to event id and do // proper filtering in trigger function - eq->triggerEvent(SceKernelEvent::Type::GfxEop, EVFILT_GRAPHICS_CORE, nullptr); + eq->TriggerEvent(GnmEventIdents::GfxEop, SceKernelEvent::Filter::GraphicsCore, nullptr); }, eq); return ORBIS_OK; @@ -455,13 +462,13 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() { s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { LOG_TRACE(Lib_GnmDriver, "called"); - ASSERT_MSG(id == SceKernelEvent::Type::GfxEop); + ASSERT_MSG(id == GnmEventIdents::GfxEop); if (!eq) { return ORBIS_KERNEL_ERROR_EBADF; } - eq->removeEvent(id); + eq->RemoveEvent(id); Platform::IrqC::Instance()->Unregister(Platform::InterruptId::GfxEop, eq); return ORBIS_OK; diff --git a/src/core/libraries/kernel/event_queue.cpp b/src/core/libraries/kernel/event_queue.cpp index 6392d078..18561d6b 100644 --- a/src/core/libraries/kernel/event_queue.cpp +++ b/src/core/libraries/kernel/event_queue.cpp @@ -8,69 +8,143 @@ namespace Libraries::Kernel { EqueueInternal::~EqueueInternal() = default; -int EqueueInternal::addEvent(const EqueueEvent& event) { +bool EqueueInternal::AddEvent(EqueueEvent& event) { std::scoped_lock lock{m_mutex}; - ASSERT(!event.isTriggered); + event.time_added = std::chrono::steady_clock::now(); - // TODO check if event is already exists and return it. Currently we just add in m_events array - m_events.push_back(event); - return 0; -} - -int EqueueInternal::removeEvent(u64 id) { - std::scoped_lock lock{m_mutex}; - - const auto& event_q = - std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); - ASSERT(event_q != m_events.cend()); - m_events.erase(event_q); - return 0; -} - -int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) { - std::unique_lock lock{m_mutex}; - int ret = 0; - - const auto predicate = [&] { - ret = getTriggeredEvents(ev, num); - return ret > 0; - }; - - if (micros == 0) { - m_cond.wait(lock, predicate); + const auto& it = std::ranges::find(m_events, event); + if (it != m_events.cend()) { + *it = std::move(event); } else { - m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); + m_events.emplace_back(std::move(event)); } - return ret; -} - -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); - } - } - } - m_cond.notify_one(); return true; } -int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) { - int ret = 0; +bool EqueueInternal::RemoveEvent(u64 id) { + bool has_found = false; + std::scoped_lock lock{m_mutex}; - for (auto& event : m_events) { - if (event.isTriggered) { - ev[ret++] = event.event; - event.reset(); + const auto& it = + std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); + if (it != m_events.cend()) { + m_events.erase(it); + has_found = true; + } + return has_found; +} + +int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { + int count = 0; + + const auto predicate = [&] { + count = GetTriggeredEvents(ev, num); + return count > 0; + }; + + if (micros == 0) { + std::unique_lock lock{m_mutex}; + m_cond.wait(lock, predicate); + } else { + std::unique_lock lock{m_mutex}; + m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); + } + + if (HasSmallTimer()) { + if (count > 0) { + const auto time_waited = std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_events[0].time_added) + .count(); + count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); + } + small_timer_event.event.data = 0; + } + + if (ev->flags & SceKernelEvent::Flags::OneShot) { + for (auto ev_id = 0u; ev_id < count; ++ev_id) { + RemoveEvent(ev->ident); } } - return ret; + return count; +} + +bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { + bool has_found = false; + { + std::scoped_lock lock{m_mutex}; + + for (auto& event : m_events) { + ASSERT_MSG(event.event.filter == filter, + "Event to trigger doesn't match to queue events"); + if (event.event.ident == ident) { + event.Trigger(trigger_data); + has_found = true; + } + } + } + m_cond.notify_one(); + return has_found; +} + +int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { + int count = 0; + + for (auto& event : m_events) { + if (event.IsTriggered()) { + if (ev->flags & SceKernelEvent::Flags::Clear) { + event.Reset(); + } + + ev[count++] = event.event; + + if (count == num) { + break; + } + } + } + + return count; +} + +bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { + // We assume that only one timer event (with the same ident across calls) + // can be posted to the queue, based on observations so far. In the opposite case, + // the small timer storage and wait logic should be reworked. + ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident); + ev.time_added = std::chrono::steady_clock::now(); + small_timer_event = std::move(ev); + return true; +} + +int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { + int count{}; + + ASSERT(num == 1); + + auto curr_clock = std::chrono::steady_clock::now(); + const auto wait_end_us = curr_clock + std::chrono::microseconds{micros}; + + do { + curr_clock = std::chrono::steady_clock::now(); + + { + std::unique_lock lock{m_mutex}; + if ((curr_clock - small_timer_event.time_added) > + std::chrono::microseconds{small_timer_event.event.data}) { + ev[count++] = small_timer_event.event; + small_timer_event.event.data = 0; + break; + } + } + + std::this_thread::yield(); + + } while (curr_clock < wait_end_us); + + return count; } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queue.h b/src/core/libraries/kernel/event_queue.h index 8fc5f5d3..30fdb41e 100644 --- a/src/core/libraries/kernel/event_queue.h +++ b/src/core/libraries/kernel/event_queue.h @@ -7,87 +7,84 @@ #include #include #include + +#include + #include "common/types.h" namespace Libraries::Kernel { -constexpr s16 EVFILT_READ = -1; -constexpr s16 EVFILT_WRITE = -2; -constexpr s16 EVFILT_AIO = -3; // attached to aio requests -constexpr s16 EVFILT_VNODE = -4; // attached to vnodes -constexpr s16 EVFILT_PROC = -5; // attached to struct proc -constexpr s16 EVFILT_SIGNAL = -6; // attached to struct proc -constexpr s16 EVFILT_TIMER = -7; // timers -constexpr s16 EVFILT_FS = -9; // filesystem events -constexpr s16 EVFILT_LIO = -10; // attached to lio requests -constexpr s16 EVFILT_USER = -11; // User events -constexpr s16 EVFILT_POLLING = -12; -constexpr s16 EVFILT_VIDEO_OUT = -13; -constexpr s16 EVFILT_GRAPHICS_CORE = -14; -constexpr s16 EVFILT_HRTIMER = -15; -constexpr s16 EVFILT_UVD_TRAP = -16; -constexpr s16 EVFILT_VCE_TRAP = -17; -constexpr s16 EVFILT_SDMA_TRAP = -18; -constexpr s16 EVFILT_REG_EV = -19; -constexpr s16 EVFILT_GPU_EXCEPTION = -20; -constexpr s16 EVFILT_GPU_SYSTEM_EXCEPTION = -21; -constexpr s16 EVFILT_GPU_DBGGC_EV = -22; -constexpr s16 EVFILT_SYSCOUNT = 22; - -constexpr u16 EV_ONESHOT = 0x10; // only report one occurrence -constexpr u16 EV_CLEAR = 0x20; // clear event state after reporting -constexpr u16 EV_RECEIPT = 0x40; // force EV_ERROR on success, data=0 -constexpr u16 EV_DISPATCH = 0x80; // disable event after reporting -constexpr u16 EV_SYSFLAGS = 0xF000; // reserved by system -constexpr u16 EV_FLAG1 = 0x2000; // filter-specific flag - class EqueueInternal; struct EqueueEvent; -using TriggerFunc = void (*)(EqueueEvent* event, void* trigger_data); -using ResetFunc = void (*)(EqueueEvent* event); -using DeleteFunc = void (*)(EqueueInternal* eq, EqueueEvent* event); - struct SceKernelEvent { - enum Type : u64 { - Compute0RelMem = 0x00, - Compute1RelMem = 0x01, - Compute2RelMem = 0x02, - Compute3RelMem = 0x03, - Compute4RelMem = 0x04, - Compute5RelMem = 0x05, - Compute6RelMem = 0x06, - GfxEop = 0x40 + enum Filter : s16 { + None = 0, + Read = -1, + Write = -2, + Aio = -3, + Vnode = -4, + Proc = -5, + Signal = -6, + Timer = -7, + Fs = -9, + Lio = -10, + User = -11, + Polling = -12, + VideoOut = -13, + GraphicsCore = -14, + HrTimer = -15, }; - u64 ident = 0; /* identifier for this event */ - s16 filter = 0; /* filter for event */ + enum Flags : u16 { + Add = 1u, + Delete = 2u, + Enable = 4u, + Disable = 8u, + OneShot = 0x10u, + Clear = 0x20u, + Receipt = 0x40u, + Dispatch = 0x80u, + Flag1 = 0x2000u, + System = 0xf000u, + }; + + u64 ident = 0; /* identifier for this event */ + Filter filter = Filter::None; /* filter for event */ u16 flags = 0; u32 fflags = 0; u64 data = 0; void* udata = nullptr; /* opaque user data identifier */ }; -struct Filter { - void* data = nullptr; -}; - struct EqueueEvent { - bool isTriggered = false; SceKernelEvent event; - Filter filter; + void* data = nullptr; + std::chrono::steady_clock::time_point time_added; + std::unique_ptr timer; - 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 { + return is_triggered; + } + + bool operator==(const EqueueEvent& ev) const { + return ev.event.ident == event.ident; + } + +private: + bool is_triggered = false; }; class EqueueInternal { @@ -97,16 +94,26 @@ public: void setName(const std::string& m_name) { this->m_name = m_name; } - int addEvent(const EqueueEvent& event); - int removeEvent(u64 id); - int waitForEvents(SceKernelEvent* ev, int num, u32 micros); - bool triggerEvent(u64 ident, s16 filter, void* trigger_data); - int getTriggeredEvents(SceKernelEvent* ev, int num); + const auto& GetName() const { + return m_name; + } + bool AddEvent(EqueueEvent& event); + bool RemoveEvent(u64 id); + int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); + bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); + int GetTriggeredEvents(SceKernelEvent* ev, int num); + + bool AddSmallTimer(EqueueEvent& event); + bool HasSmallTimer() const { + return small_timer_event.event.data != 0; + } + int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); private: std::string m_name; std::mutex m_mutex; std::vector m_events; + EqueueEvent small_timer_event{}; std::condition_variable m_cond; }; diff --git a/src/core/libraries/kernel/event_queues.cpp b/src/core/libraries/kernel/event_queues.cpp index e2b151a8..aee4613c 100644 --- a/src/core/libraries/kernel/event_queues.cpp +++ b/src/core/libraries/kernel/event_queues.cpp @@ -2,12 +2,29 @@ // 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" +#include + namespace Libraries::Kernel { +extern boost::asio::io_context io_context; +extern void KernelSignalRequest(); + +static constexpr auto HrTimerSpinlockThresholdUs = 1200u; + +static void SmallTimerCallback(const boost::system::error_code& error, SceKernelEqueue eq, + 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) { if (eq == nullptr) { LOG_ERROR(Kernel_Event, "Event queue is null!"); @@ -43,31 +60,39 @@ 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_TRACE(Kernel_Event, "equeue = {} num = {}", eq->GetName(), num); if (eq == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; } if (ev == nullptr) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } if (num < 1) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } - if (timo == nullptr) { // wait until an event arrives without timing out - *out = eq->waitForEvents(ev, num, 0); - } + if (eq->HasSmallTimer()) { + ASSERT(timo && *timo); + *out = eq->WaitForSmallTimer(ev, num, *timo); + } else { + if (timo == nullptr) { // wait until an event arrives without timing out + *out = eq->WaitForEvents(ev, num, 0); + } - if (timo != nullptr) { - // Only events that have already arrived at the time of this function call can be received - if (*timo == 0) { - *out = eq->getTriggeredEvents(ev, num); - } else { - // Wait until an event arrives with timing out - *out = eq->waitForEvents(ev, num, *timo); + if (timo != nullptr) { + // Only events that have already arrived at the time of this function call can be + // received + if (*timo == 0) { + *out = eq->GetTriggeredEvents(ev, num); + } else { + // Wait until an event arrives with timing out + *out = eq->WaitForEvents(ev, num, *timo); + } } } @@ -78,21 +103,65 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int return ORBIS_OK; } +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 resolution + const auto total_us = ts->tv_sec * 1000'000 + ts->tv_nsec / 1000; + + EqueueEvent event{}; + event.event.ident = id; + event.event.filter = SceKernelEvent::Filter::HrTimer; + event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::OneShot; + event.event.fflags = 0; + event.event.data = total_us; + event.event.udata = udata; + + // HR timers cannot be implemented within the existing event queue architecture due to the + // slowness of the notification mechanism. For instance, a 100us timer will lose its precision + // as the trigger time drifts by +50-700%, depending on the host PC and workload. To address + // this issue, we use a spinlock for small waits (which can be adjusted using + // `HrTimerSpinlockThresholdUs`) and fall back to boost asio timers if the time to tick is + // large. Even for large delays, we truncate a small portion to complete the wait + // using the spinlock, prioritizing precision. + if (total_us < HrTimerSpinlockThresholdUs) { + return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; + } + + event.timer = std::make_unique( + io_context, std::chrono::microseconds(total_us - HrTimerSpinlockThresholdUs)); + + event.timer->async_wait( + std::bind(SmallTimerCallback, boost::asio::placeholders::error, eq, event.event)); + + if (!eq->AddEvent(event)) { + return ORBIS_KERNEL_ERROR_ENOMEM; + } + + KernelSignalRequest(); + + return ORBIS_OK; +} + int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) { if (eq == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; } - Kernel::EqueueEvent event{}; - event.isTriggered = false; + EqueueEvent event{}; event.event.ident = id; - event.event.filter = Kernel::EVFILT_USER; + event.event.filter = SceKernelEvent::Filter::User; event.event.udata = 0; - event.event.flags = 1; + event.event.flags = SceKernelEvent::Flags::Add; event.event.fflags = 0; event.event.data = 0; - return eq->addEvent(event); + return eq->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; } int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) { @@ -100,33 +169,41 @@ int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) { return ORBIS_KERNEL_ERROR_EBADF; } - Kernel::EqueueEvent event{}; - event.isTriggered = false; + EqueueEvent event{}; event.event.ident = id; - event.event.filter = Kernel::EVFILT_USER; + event.event.filter = SceKernelEvent::Filter::User; event.event.udata = 0; - event.event.flags = 0x21; + event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::Clear; event.event.fflags = 0; event.event.data = 0; - return eq->addEvent(event); + return eq->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; } void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev) { - if (!ev) { - return nullptr; - } - + ASSERT(ev); return ev->udata; } int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata) { - eq->triggerEvent(id, Kernel::EVFILT_USER, udata); + if (eq == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (!eq->TriggerEvent(id, SceKernelEvent::Filter::User, udata)) { + return ORBIS_KERNEL_ERROR_ENOENT; + } return ORBIS_OK; } int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { - eq->removeEvent(id); + if (eq == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (!eq->RemoveEvent(id)) { + return ORBIS_KERNEL_ERROR_ENOENT; + } return ORBIS_OK; } 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 ce17a3f2..2eeb997f 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -3,9 +3,14 @@ #include #include + +#include + #include "common/assert.h" #include "common/logging/log.h" #include "common/singleton.h" +#include "common/thread.h" +#include "core/file_format/psf.h" #include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/cpu_management.h" @@ -19,6 +24,7 @@ #include "core/libraries/libs.h" #include "core/linker.h" #include "core/memory.h" + #ifdef _WIN64 #include #include @@ -26,12 +32,43 @@ #else #include #endif -#include namespace Libraries::Kernel { static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return +boost::asio::io_context io_context; +std::mutex m_asio_req; +std::condition_variable_any cv_asio_req; +std::atomic asio_requests; +std::jthread service_thread; + +void KernelSignalRequest() { + std::unique_lock lock{m_asio_req}; + ++asio_requests; + cv_asio_req.notify_one(); +} + +static void KernelServiceThread(std::stop_token stoken) { + Common::SetCurrentThreadName("Kernel_ServiceThread"); + + while (!stoken.stop_requested()) { + HLE_TRACE; + { + std::unique_lock lock{m_asio_req}; + cv_asio_req.wait(lock, stoken, [] { return asio_requests != 0; }); + } + if (stoken.stop_requested()) { + break; + } + + io_context.run(); + io_context.reset(); + + asio_requests = 0; + } +} + static void* PS4_SYSV_ABI sceKernelGetProcParam() { auto* linker = Common::Singleton::Instance(); return reinterpret_cast(linker->GetProcParam()); @@ -295,8 +332,8 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { return 0; } -char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { - char* path = "sys"; +const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { + const char* path = "sys"; return path; } @@ -310,6 +347,8 @@ int PS4_SYSV_ABI _sigprocmask() { } void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { + service_thread = std::jthread{KernelServiceThread}; + // obj LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); // misc @@ -353,6 +392,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/driver.cpp b/src/core/libraries/videoout/driver.cpp index 7700a3a1..ece4ea01 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -196,7 +196,7 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) { // Trigger flip events for the port. for (auto& event : req.port->flip_events) { if (event != nullptr) { - event->triggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::EVFILT_VIDEO_OUT, + event->TriggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::SceKernelEvent::Filter::VideoOut, reinterpret_cast(req.flip_arg)); } } @@ -252,7 +252,8 @@ void VideoOutDriver::Vblank() { // Trigger flip events for the port. for (auto& event : main_port.vblank_events) { if (event != nullptr) { - event->triggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK, Kernel::EVFILT_VIDEO_OUT, nullptr); + event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK, + Kernel::SceKernelEvent::Filter::VideoOut, nullptr); } } } diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 7a0d237c..51cfcf4c 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -48,16 +48,15 @@ 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.filter = Kernel::SceKernelEvent::Filter::VideoOut; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; - event.filter.data = port; + event.data = port; port->flip_events.push_back(eq); - return eq->addEvent(event); + return eq->AddEvent(event); } s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) { @@ -73,16 +72,15 @@ 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.filter = Kernel::SceKernelEvent::Filter::VideoOut; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; - event.filter.data = port; + event.data = port; port->vblank_events.push_back(eq); - return eq->addEvent(event); + return eq->AddEvent(event); } s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses,