Merge pull request #277 from shadps4-emu/kernel/hr_timers
HR Timers support and event queue refactoring
This commit is contained in:
commit
59be090c83
|
@ -31,9 +31,6 @@
|
||||||
[submodule "externals/robin-map"]
|
[submodule "externals/robin-map"]
|
||||||
path = externals/robin-map
|
path = externals/robin-map
|
||||||
url = https://github.com/Tessil/robin-map.git
|
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"]
|
[submodule "externals/xbyak"]
|
||||||
path = externals/xbyak
|
path = externals/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
url = https://github.com/herumi/xbyak.git
|
||||||
|
@ -58,3 +55,6 @@
|
||||||
[submodule "externals/tracy"]
|
[submodule "externals/tracy"]
|
||||||
path = externals/tracy
|
path = externals/tracy
|
||||||
url = https://github.com/shadps4-emu/tracy.git
|
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
|
||||||
|
|
|
@ -576,6 +576,8 @@ if (WIN32)
|
||||||
target_sources(shadps4 PRIVATE src/shadps4.rc)
|
target_sources(shadps4 PRIVATE src/shadps4.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_definitions(-DBOOST_ASIO_STANDALONE)
|
||||||
|
|
||||||
target_include_directories(shadps4 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(shadps4 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
# Shaders sources
|
# Shaders sources
|
||||||
|
|
|
@ -12,8 +12,8 @@ endif()
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if (NOT TARGET Boost::headers)
|
if (NOT TARGET Boost::headers)
|
||||||
set(BOOST_ROOT "${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/boost" CACHE STRING "")
|
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "")
|
||||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||||
add_library(boost INTERFACE)
|
add_library(boost INTERFACE)
|
||||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 87b7817119982e8ad6068855fae31b11590514be
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 147b2de7734f5dc3b9aeb1f4135ae15fcd44b9d7
|
|
@ -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) \
|
||||||
|
|
|
@ -170,7 +170,7 @@ public:
|
||||||
const char* function, std::string message) {
|
const char* function, std::string message) {
|
||||||
// Propagate important log messages to the profiler
|
// Propagate important log messages to the profiler
|
||||||
if (IsProfilerConnected()) {
|
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) {
|
switch (log_level) {
|
||||||
case Level::Warning:
|
case Level::Warning:
|
||||||
TRACE_WARN(msg_str);
|
TRACE_WARN(msg_str);
|
||||||
|
|
|
@ -26,6 +26,17 @@ using namespace AmdGpu;
|
||||||
|
|
||||||
static std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
static std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
||||||
|
|
||||||
|
enum GnmEventIdents : u64 {
|
||||||
|
Compute0RelMem = 0x00,
|
||||||
|
Compute1RelMem = 0x01,
|
||||||
|
Compute2RelMem = 0x02,
|
||||||
|
Compute3RelMem = 0x03,
|
||||||
|
Compute4RelMem = 0x04,
|
||||||
|
Compute5RelMem = 0x05,
|
||||||
|
Compute6RelMem = 0x06,
|
||||||
|
GfxEop = 0x40
|
||||||
|
};
|
||||||
|
|
||||||
enum ShaderStages : u32 {
|
enum ShaderStages : u32 {
|
||||||
Cs,
|
Cs,
|
||||||
Ps,
|
Ps,
|
||||||
|
@ -327,10 +338,6 @@ static inline u32* ClearContextState(u32* cmdbuf) {
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
|
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
|
||||||
LOG_TRACE(Lib_GnmDriver, "called");
|
LOG_TRACE(Lib_GnmDriver, "called");
|
||||||
if (id != SceKernelEvent::Type::GfxEop) {
|
|
||||||
return ORBIS_OK;
|
|
||||||
}
|
|
||||||
ASSERT_MSG(id == SceKernelEvent::Type::GfxEop);
|
|
||||||
|
|
||||||
if (!eq) {
|
if (!eq) {
|
||||||
return ORBIS_KERNEL_ERROR_EBADF;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
|
@ -338,20 +345,20 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
|
||||||
|
|
||||||
EqueueEvent kernel_event{};
|
EqueueEvent kernel_event{};
|
||||||
kernel_event.event.ident = id;
|
kernel_event.event.ident = id;
|
||||||
kernel_event.event.filter = EVFILT_GRAPHICS_CORE;
|
kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore;
|
||||||
kernel_event.event.flags = 1;
|
kernel_event.event.flags = SceKernelEvent::Flags::Add;
|
||||||
kernel_event.event.fflags = 0;
|
kernel_event.event.fflags = 0;
|
||||||
kernel_event.event.data = id;
|
kernel_event.event.data = id;
|
||||||
kernel_event.event.udata = udata;
|
kernel_event.event.udata = udata;
|
||||||
eq->addEvent(kernel_event);
|
eq->AddEvent(kernel_event);
|
||||||
|
|
||||||
Platform::IrqC::Instance()->Register(
|
Platform::IrqC::Instance()->Register(
|
||||||
Platform::InterruptId::GfxEop,
|
Platform::InterruptId::GfxEop,
|
||||||
[=](Platform::InterruptId irq) {
|
[=](Platform::InterruptId irq) {
|
||||||
ASSERT_MSG(irq == Platform::InterruptId::GfxEop,
|
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
|
// proper filtering in trigger function
|
||||||
eq->triggerEvent(SceKernelEvent::Type::GfxEop, EVFILT_GRAPHICS_CORE, nullptr);
|
eq->TriggerEvent(GnmEventIdents::GfxEop, SceKernelEvent::Filter::GraphicsCore, nullptr);
|
||||||
},
|
},
|
||||||
eq);
|
eq);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
|
@ -455,13 +462,13 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() {
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
|
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
|
||||||
LOG_TRACE(Lib_GnmDriver, "called");
|
LOG_TRACE(Lib_GnmDriver, "called");
|
||||||
ASSERT_MSG(id == SceKernelEvent::Type::GfxEop);
|
ASSERT_MSG(id == GnmEventIdents::GfxEop);
|
||||||
|
|
||||||
if (!eq) {
|
if (!eq) {
|
||||||
return ORBIS_KERNEL_ERROR_EBADF;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
eq->removeEvent(id);
|
eq->RemoveEvent(id);
|
||||||
|
|
||||||
Platform::IrqC::Instance()->Unregister(Platform::InterruptId::GfxEop, eq);
|
Platform::IrqC::Instance()->Unregister(Platform::InterruptId::GfxEop, eq);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
|
|
|
@ -8,69 +8,143 @@ namespace Libraries::Kernel {
|
||||||
|
|
||||||
EqueueInternal::~EqueueInternal() = default;
|
EqueueInternal::~EqueueInternal() = default;
|
||||||
|
|
||||||
int EqueueInternal::addEvent(const EqueueEvent& event) {
|
bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
||||||
std::scoped_lock lock{m_mutex};
|
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
|
const auto& it = std::ranges::find(m_events, event);
|
||||||
m_events.push_back(event);
|
if (it != m_events.cend()) {
|
||||||
return 0;
|
*it = std::move(event);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
} else {
|
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
|
bool EqueueInternal::RemoveEvent(u64 id) {
|
||||||
int ret = 0;
|
bool has_found = false;
|
||||||
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
|
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::microseconds>(
|
||||||
|
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 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) {
|
for (auto& event : m_events) {
|
||||||
if (event.isTriggered) {
|
ASSERT_MSG(event.event.filter == filter,
|
||||||
ev[ret++] = event.event;
|
"Event to trigger doesn't match to queue events");
|
||||||
event.reset();
|
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 ret;
|
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
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -7,87 +7,84 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/asio/steady_timer.hpp>
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
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;
|
class EqueueInternal;
|
||||||
struct EqueueEvent;
|
struct EqueueEvent;
|
||||||
|
|
||||||
using TriggerFunc = void (*)(EqueueEvent* event, void* trigger_data);
|
|
||||||
using ResetFunc = void (*)(EqueueEvent* event);
|
|
||||||
using DeleteFunc = void (*)(EqueueInternal* eq, EqueueEvent* event);
|
|
||||||
|
|
||||||
struct SceKernelEvent {
|
struct SceKernelEvent {
|
||||||
enum Type : u64 {
|
enum Filter : s16 {
|
||||||
Compute0RelMem = 0x00,
|
None = 0,
|
||||||
Compute1RelMem = 0x01,
|
Read = -1,
|
||||||
Compute2RelMem = 0x02,
|
Write = -2,
|
||||||
Compute3RelMem = 0x03,
|
Aio = -3,
|
||||||
Compute4RelMem = 0x04,
|
Vnode = -4,
|
||||||
Compute5RelMem = 0x05,
|
Proc = -5,
|
||||||
Compute6RelMem = 0x06,
|
Signal = -6,
|
||||||
GfxEop = 0x40
|
Timer = -7,
|
||||||
|
Fs = -9,
|
||||||
|
Lio = -10,
|
||||||
|
User = -11,
|
||||||
|
Polling = -12,
|
||||||
|
VideoOut = -13,
|
||||||
|
GraphicsCore = -14,
|
||||||
|
HrTimer = -15,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 */
|
u64 ident = 0; /* identifier for this event */
|
||||||
s16 filter = 0; /* filter for event */
|
Filter filter = Filter::None; /* filter for event */
|
||||||
u16 flags = 0;
|
u16 flags = 0;
|
||||||
u32 fflags = 0;
|
u32 fflags = 0;
|
||||||
u64 data = 0;
|
u64 data = 0;
|
||||||
void* udata = nullptr; /* opaque user data identifier */
|
void* udata = nullptr; /* opaque user data identifier */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Filter {
|
|
||||||
void* data = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EqueueEvent {
|
struct EqueueEvent {
|
||||||
bool isTriggered = false;
|
|
||||||
SceKernelEvent event;
|
SceKernelEvent event;
|
||||||
Filter filter;
|
void* data = nullptr;
|
||||||
|
std::chrono::steady_clock::time_point time_added;
|
||||||
|
std::unique_ptr<boost::asio::steady_timer> timer;
|
||||||
|
|
||||||
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 {
|
||||||
|
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,16 +94,26 @@ 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 {
|
||||||
int removeEvent(u64 id);
|
return m_name;
|
||||||
int waitForEvents(SceKernelEvent* ev, int num, u32 micros);
|
}
|
||||||
bool triggerEvent(u64 ident, s16 filter, void* trigger_data);
|
bool AddEvent(EqueueEvent& event);
|
||||||
int getTriggeredEvents(SceKernelEvent* ev, int num);
|
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:
|
private:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::vector<EqueueEvent> m_events;
|
std::vector<EqueueEvent> m_events;
|
||||||
|
EqueueEvent small_timer_event{};
|
||||||
std::condition_variable m_cond;
|
std::condition_variable m_cond;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,29 @@
|
||||||
// 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"
|
||||||
|
|
||||||
|
#include <boost/asio/placeholders.hpp>
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
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) {
|
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
|
||||||
if (eq == nullptr) {
|
if (eq == nullptr) {
|
||||||
LOG_ERROR(Kernel_Event, "Event queue is null!");
|
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,
|
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_TRACE(Kernel_Event, "equeue = {} num = {}", eq->GetName(), num);
|
||||||
|
|
||||||
if (eq == nullptr) {
|
if (eq == nullptr) {
|
||||||
return ORBIS_KERNEL_ERROR_EBADF;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev == nullptr) {
|
if (ev == nullptr) {
|
||||||
return SCE_KERNEL_ERROR_EFAULT;
|
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num < 1) {
|
if (num < 1) {
|
||||||
return SCE_KERNEL_ERROR_EINVAL;
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eq->HasSmallTimer()) {
|
||||||
|
ASSERT(timo && *timo);
|
||||||
|
*out = eq->WaitForSmallTimer(ev, num, *timo);
|
||||||
|
} else {
|
||||||
if (timo == nullptr) { // wait until an event arrives without timing out
|
if (timo == nullptr) { // wait until an event arrives without timing out
|
||||||
*out = eq->waitForEvents(ev, num, 0);
|
*out = eq->WaitForEvents(ev, num, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timo != nullptr) {
|
if (timo != nullptr) {
|
||||||
// Only events that have already arrived at the time of this function call can be received
|
// Only events that have already arrived at the time of this function call can be
|
||||||
|
// received
|
||||||
if (*timo == 0) {
|
if (*timo == 0) {
|
||||||
*out = eq->getTriggeredEvents(ev, num);
|
*out = eq->GetTriggeredEvents(ev, num);
|
||||||
} else {
|
} else {
|
||||||
// Wait until an event arrives with timing out
|
// Wait until an event arrives with timing out
|
||||||
*out = eq->waitForEvents(ev, num, *timo);
|
*out = eq->WaitForEvents(ev, num, *timo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,21 +103,65 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
|
||||||
return ORBIS_OK;
|
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<boost::asio::steady_timer>(
|
||||||
|
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) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::EqueueEvent event{};
|
EqueueEvent event{};
|
||||||
event.isTriggered = false;
|
|
||||||
event.event.ident = id;
|
event.event.ident = id;
|
||||||
event.event.filter = Kernel::EVFILT_USER;
|
event.event.filter = SceKernelEvent::Filter::User;
|
||||||
event.event.udata = 0;
|
event.event.udata = 0;
|
||||||
event.event.flags = 1;
|
event.event.flags = SceKernelEvent::Flags::Add;
|
||||||
event.event.fflags = 0;
|
event.event.fflags = 0;
|
||||||
event.event.data = 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) {
|
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;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::EqueueEvent event{};
|
EqueueEvent event{};
|
||||||
event.isTriggered = false;
|
|
||||||
event.event.ident = id;
|
event.event.ident = id;
|
||||||
event.event.filter = Kernel::EVFILT_USER;
|
event.event.filter = SceKernelEvent::Filter::User;
|
||||||
event.event.udata = 0;
|
event.event.udata = 0;
|
||||||
event.event.flags = 0x21;
|
event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::Clear;
|
||||||
event.event.fflags = 0;
|
event.event.fflags = 0;
|
||||||
event.event.data = 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) {
|
void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev) {
|
||||||
if (!ev) {
|
ASSERT(ev);
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ev->udata;
|
return ev->udata;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* 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;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) {
|
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;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -3,9 +3,14 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
#include "common/thread.h"
|
||||||
|
#include "core/file_format/psf.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/kernel/cpu_management.h"
|
#include "core/libraries/kernel/cpu_management.h"
|
||||||
|
@ -19,6 +24,7 @@
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "core/linker.h"
|
#include "core/linker.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
|
@ -26,12 +32,43 @@
|
||||||
#else
|
#else
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
#include <core/file_format/psf.h>
|
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return
|
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<u32> 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() {
|
static void* PS4_SYSV_ABI sceKernelGetProcParam() {
|
||||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||||
return reinterpret_cast<void*>(linker->GetProcParam());
|
return reinterpret_cast<void*>(linker->GetProcParam());
|
||||||
|
@ -295,8 +332,8 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
|
const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
|
||||||
char* path = "sys";
|
const char* path = "sys";
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +347,8 @@ int PS4_SYSV_ABI _sigprocmask() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
||||||
|
service_thread = std::jthread{KernelServiceThread};
|
||||||
|
|
||||||
// obj
|
// obj
|
||||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
||||||
// misc
|
// misc
|
||||||
|
@ -353,6 +392,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);
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,7 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
||||||
// Trigger flip events for the port.
|
// Trigger flip events for the port.
|
||||||
for (auto& event : req.port->flip_events) {
|
for (auto& event : req.port->flip_events) {
|
||||||
if (event != nullptr) {
|
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<void*>(req.flip_arg));
|
reinterpret_cast<void*>(req.flip_arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,8 @@ void VideoOutDriver::Vblank() {
|
||||||
// Trigger flip events for the port.
|
// Trigger flip events for the port.
|
||||||
for (auto& event : main_port.vblank_events) {
|
for (auto& event : main_port.vblank_events) {
|
||||||
if (event != nullptr) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,16 +48,15 @@ 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::SceKernelEvent::Filter::VideoOut;
|
||||||
event.event.udata = udata;
|
event.event.udata = udata;
|
||||||
event.event.fflags = 0;
|
event.event.fflags = 0;
|
||||||
event.event.data = 0;
|
event.event.data = 0;
|
||||||
event.filter.data = port;
|
event.data = port;
|
||||||
|
|
||||||
port->flip_events.push_back(eq);
|
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) {
|
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{};
|
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::SceKernelEvent::Filter::VideoOut;
|
||||||
event.event.udata = udata;
|
event.event.udata = udata;
|
||||||
event.event.fflags = 0;
|
event.event.fflags = 0;
|
||||||
event.event.data = 0;
|
event.event.data = 0;
|
||||||
event.filter.data = port;
|
event.data = port;
|
||||||
|
|
||||||
port->vblank_events.push_back(eq);
|
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,
|
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses,
|
||||||
|
|
Loading…
Reference in New Issue