From cefb5a9fc0f3db8f634dfd533810adb15786a3a7 Mon Sep 17 00:00:00 2001 From: TideOPL Date: Wed, 7 Oct 2026 10:15:39 +0100 Subject: [PATCH 1/6] docs(libScePad): record the scePadRead queue gap in technical debt Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libScePad/Export.cpp | 9 ++++----- docs/dev/TechnicalDebt.md | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index c426e5067..bc9464c19 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -142,11 +142,10 @@ int APS5_VABI scePadOpen_nid_postfix(int userId, int type, int index, const void int APS5_VABI scePadReadState(int handle, PadData* data); int APS5_VABI scePadRead_nid_postfix(int handle, PadData* data, int num) { - // The title drains the queued states; one current state is reported per call. - if (data == nullptr || num <= 0) APS5_INVALID_ARG_EX; - const int result = scePadReadState(handle, data); - if (result != 0) return result; - return 1; + if (data == nullptr || num <= 0) APS5_INVALID_ARG_EX; + const int result = scePadReadState(handle, data); + if (result != 0) return result; + return 1; } int APS5_VABI scePadReadState(int handle, PadData* data) { diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 57ce5607d..7154cc035 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -233,3 +233,4 @@ Throughout the project, every function at every stage either **does exactly what - [sceFontOpenFontSet](../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) opens only the primary file of a system font set. Characters the console takes from the set's other files (Hangul, Thai and Arabic in Japanese and Chinese sets, Arabic in European sets) are reported as unsupported glyphs. The console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans CJK) is loaded instead. It is not the system font: its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [Font writing lines](../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontCreateWritingLine`, `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics` and `sceFontWritingLineClear` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libFont.cpp): runs are placed left to right, each at the line's advance so far, with no spacing or adjustment. Only horizontal and left-to-right lines, writing attribute 0 and non-null run metrics are modelled; other values, the metrics of an empty line and unknown or null handles throw, since no reference gives their result or error code. `sceFontWritingLineGetOrderingSpace` still throws (KytyPS5's values are placeholders). Lines are allocated on the host, not in the font memory - [sceImeKeyboardGetResourceId](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) reports that no USB keyboard is connected (`0x80bc0004`, as [shadPS4](https://github.com/shadps4-emu/shadPS4) does) because IME keyboard events are not delivered to the handler, although [libSceKeyboard](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) exposes the host keyboard +- [scePadRead](../../core/libs/prx/libScePad/Export.cpp) (libScePad) keeps no queue of past states: each call writes the current state to the first entry and returns 1 whatever `num` is, so input that changes and changes back between two calls is not reported From efa5afbfbd9bed76551b81508fe61c5bef6a3976 Mon Sep 17 00:00:00 2001 From: TideOPL Date: Wed, 7 Oct 2026 11:03:30 +0100 Subject: [PATCH 2/6] fix(libkernel): read the process start before the clock in process time sceKernelGetProcessTime and sceKernelGetProcessTimeCounter subtracted GetStartNanos() from GetMonotonicNanos() in one expression. GCC reads the clock first, so on the first call in a process the start was captured after it and the result wrapped to about 2^64. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libkernel/CMakeLists.txt | 10 ++++++++++ core/libs/prx/libkernel/Time/Time.cpp | 6 ++++-- core/libs/tests/GuestProcessTime.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 core/libs/tests/GuestProcessTime.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index a9521483b..475dfb4be 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -90,6 +90,16 @@ if(BUILD_TESTING) set_tests_properties(guest_posix_sem_init guest_posix_sem_post PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_process_time_tests ${LIBS_INCLUDE_DIR}/tests/GuestProcessTime.cpp) + target_include_directories(guest_process_time_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_process_time_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_process_time_tests) + add_test(NAME guest_process_time COMMAND guest_process_time_tests) + add_test(NAME guest_process_time_counter COMMAND guest_process_time_tests counter) + if(WIN32) + set_tests_properties(guest_process_time guest_process_time_counter PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_library(guest_module_fixture SHARED ${LIBS_INCLUDE_DIR}/tests/GuestModuleFixture.cpp) target_include_directories(guest_module_fixture PRIVATE ${LIBS_INCLUDE_DIR}) add_custom_command(TARGET guest_module_fixture POST_BUILD diff --git a/core/libs/prx/libkernel/Time/Time.cpp b/core/libs/prx/libkernel/Time/Time.cpp index 395f85829..521328a18 100644 --- a/core/libs/prx/libkernel/Time/Time.cpp +++ b/core/libs/prx/libkernel/Time/Time.cpp @@ -147,11 +147,13 @@ static const bool g_timerSelfTest = [] { extern "C" { std::uint64_t APS5_VABI sceKernelGetProcessTime() { - return (GetMonotonicNanos() - GetStartNanos()) / 1000ULL; + const std::uint64_t start = GetStartNanos(); + return (GetMonotonicNanos() - start) / 1000ULL; } std::uint64_t APS5_VABI sceKernelGetProcessTimeCounter() { - return GetMonotonicNanos() - GetStartNanos(); + const std::uint64_t start = GetStartNanos(); + return GetMonotonicNanos() - start; } std::uint64_t APS5_VABI sceKernelGetProcessTimeCounterFrequency() { diff --git a/core/libs/tests/GuestProcessTime.cpp b/core/libs/tests/GuestProcessTime.cpp new file mode 100644 index 000000000..cb8a6186e --- /dev/null +++ b/core/libs/tests/GuestProcessTime.cpp @@ -0,0 +1,25 @@ +#include "prx/libkernel/Time/include/Time.hpp" +#include +#include +#include +#include + +static void Require(bool condition, const char* message) { + if (!condition) { + std::fprintf(stderr, "%s\n", message); + std::abort(); + } +} + +// The first call in a process also fixes the process start, so it must not read the clock before it. +int main(int argc, char** argv) { + constexpr std::uint64_t minuteMicros = 60ULL * 1000 * 1000; + if (argc > 1 && std::strcmp(argv[1], "counter") == 0) { + Require(sceKernelGetProcessTimeCounter() < minuteMicros * 1000, "first process time counter wrapped"); + return 0; + } + const std::uint64_t first = sceKernelGetProcessTime(); + Require(first < minuteMicros, "first process time wrapped"); + Require(sceKernelGetProcessTime() >= first, "process time went backwards"); + return 0; +} From efa6cd56f08fa4b4b0b90096355023ae64136743 Mon Sep 17 00:00:00 2001 From: TideOPL Date: Wed, 7 Oct 2026 11:03:30 +0100 Subject: [PATCH 3/6] fix(libScePad): queue input states for scePadRead scePadRead reported only the current state, so input that changed and changed back between two reads was lost. Published states are now kept in a queue of up to 64 and drained oldest first; with none queued the current state is returned as before. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libScePad/Export.cpp | 7 +-- core/libs/prx/libScePad/include/PadState.hpp | 2 + core/libs/prx/libScePad/src/PadState.cpp | 50 +++++++++++++++++--- core/libs/tests/GuestPad.cpp | 17 +++++++ docs/dev/TechnicalDebt.md | 1 - 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index bc9464c19..0d20fed00 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -139,13 +139,10 @@ int APS5_VABI scePadOpen_nid_postfix(int userId, int type, int index, const void return PAD_HANDLE; } -int APS5_VABI scePadReadState(int handle, PadData* data); - int APS5_VABI scePadRead_nid_postfix(int handle, PadData* data, int num) { + if (handle != PAD_HANDLE) return PAD_ERROR_INVALID_HANDLE; if (data == nullptr || num <= 0) APS5_INVALID_ARG_EX; - const int result = scePadReadState(handle, data); - if (result != 0) return result; - return 1; + return Pad::Read(data, num); } int APS5_VABI scePadReadState(int handle, PadData* data) { diff --git a/core/libs/prx/libScePad/include/PadState.hpp b/core/libs/prx/libScePad/include/PadState.hpp index c23b02c4d..1d2205fa6 100644 --- a/core/libs/prx/libScePad/include/PadState.hpp +++ b/core/libs/prx/libScePad/include/PadState.hpp @@ -51,6 +51,8 @@ struct PadOutputState { namespace Pad { void Initialize(); PadData ReadState(); +// Drains up to `num` queued states, oldest first; with none queued, writes the current state. Returns the count written. +int Read(PadData* data, int num); void SetVibration(std::uint8_t large, std::uint8_t small); void SetVibrationMode(int mode); void SetLightBar(bool valid, std::uint8_t r, std::uint8_t g, std::uint8_t b); diff --git a/core/libs/prx/libScePad/src/PadState.cpp b/core/libs/prx/libScePad/src/PadState.cpp index ea3e212d4..3da6c4f21 100644 --- a/core/libs/prx/libScePad/src/PadState.cpp +++ b/core/libs/prx/libScePad/src/PadState.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -12,6 +13,14 @@ namespace { PadInputState state; PadOutputState output; std::uint64_t timestamp = 0; + + struct QueuedInput { + PadInputState input; + std::uint64_t timestamp = 0; + }; + // States published since the last scePadRead; the oldest is dropped once 64 are waiting. + constexpr std::size_t kQueueCapacity = 64; + std::deque queue; std::exception_ptr failure; bool initialized = false; @@ -93,15 +102,14 @@ void Pad::Initialize() { if (!initialized) { timestamp = sceKernelGetProcessTime(); lastFuseTime = timestamp; + queue.clear(); } initialized = true; } -PadData Pad::ReadState() { - std::lock_guard lock(stateMutex); - if (failure) std::rethrow_exception(failure); - if (!initialized) throw std::runtime_error("Pad: read before initialization"); - const std::uint64_t now = sceKernelGetProcessTime(); +namespace { +// Converts one input sample; motion fusion integrates up to `now` and touch ids advance per call. +PadData BuildData(const PadInputState& state, std::uint64_t changeTime, std::uint64_t now) { PadData data{}; data.buttons = state.buttons; data.left_stick_x = state.sticks[0]; @@ -123,7 +131,7 @@ PadData Pad::ReadState() { orientation = Quat{}; biasIntegral[0] = biasIntegral[1] = biasIntegral[2] = 0.0f; } - lastFuseTime = now; + lastFuseTime = std::max(lastFuseTime, now); data.orientation_x = orientation.x; data.orientation_y = orientation.y; data.orientation_z = orientation.z; @@ -138,7 +146,7 @@ PadData Pad::ReadState() { data.connected = true; data.connected_count = 1; - data.timestamp = timestamp; + data.timestamp = changeTime; std::uint8_t touchNum = 0; for (int i = 0; i < 2; ++i) { @@ -163,6 +171,32 @@ PadData Pad::ReadState() { data.touch_data_touch_num = touchNum; return data; } +} + +PadData Pad::ReadState() { + std::lock_guard lock(stateMutex); + if (failure) std::rethrow_exception(failure); + if (!initialized) throw std::runtime_error("Pad: read before initialization"); + return BuildData(state, timestamp, sceKernelGetProcessTime()); +} + +int Pad::Read(PadData* data, int num) { + if (data == nullptr || num <= 0) throw std::invalid_argument("Pad: invalid read buffer"); + std::lock_guard lock(stateMutex); + if (failure) std::rethrow_exception(failure); + if (!initialized) throw std::runtime_error("Pad: read before initialization"); + if (queue.empty()) { + data[0] = BuildData(state, timestamp, sceKernelGetProcessTime()); + return 1; + } + int count = 0; + while (count < num && !queue.empty()) { + const QueuedInput& entry = queue.front(); + data[count++] = BuildData(entry.input, entry.timestamp, entry.timestamp); + queue.pop_front(); + } + return count; +} void Pad::SetVibration(std::uint8_t large, std::uint8_t small) { std::lock_guard lock(stateMutex); @@ -280,6 +314,8 @@ extern "C" void PadPublishInput_nid_postfix(const PadInputState& input) { state.touch == input.touch && state.deviceKind == input.deviceKind) return; state = input; timestamp = sceKernelGetProcessTime(); + if (queue.size() == kQueueCapacity) queue.pop_front(); + queue.push_back({input, timestamp}); } extern "C" void PadReportInputFailure_nid_postfix(std::exception_ptr error) { diff --git a/core/libs/tests/GuestPad.cpp b/core/libs/tests/GuestPad.cpp index 866c44ba7..68c23fd6e 100644 --- a/core/libs/tests/GuestPad.cpp +++ b/core/libs/tests/GuestPad.cpp @@ -59,6 +59,22 @@ static void CheckTouchContact() { Pad::ReadState(); } +static void CheckReadQueue(int handle) { + PadData drained[64]{}; + scePadRead_nid_postfix(handle, drained, 64); + PadInputState pressed; + pressed.buttons = static_cast(Pad::PadButton::Cross); + PadPublishInput_nid_postfix(pressed); + PadPublishInput_nid_postfix(PadInputState{}); + PadData data[4]{}; + Require(scePadRead_nid_postfix(handle, data, 4) == 2); + Require((data[0].buttons & pressed.buttons) != 0); + Require((data[1].buttons & pressed.buttons) == 0); + Require(data[0].timestamp <= data[1].timestamp); + Require(scePadRead_nid_postfix(handle, data, 4) == 1); + Require(data[0].buttons == 0); +} + static void CheckReadStateHandle(int handle) { PadData data{}; Require(scePadReadState(0, &data) == PAD_ERROR_INVALID_HANDLE); @@ -81,6 +97,7 @@ int main() { Require(scePadGetHandle(user, 2, 0) == handle); CheckTiltCorrection(handle); CheckTouchContact(); + CheckReadQueue(handle); CheckReadStateHandle(handle); Require(scePadGetHandle(0xff, 16, 0) == handle); Require(scePadGetHandle(user, 16, 0) == noHandle); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 7154cc035..57ce5607d 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -233,4 +233,3 @@ Throughout the project, every function at every stage either **does exactly what - [sceFontOpenFontSet](../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) opens only the primary file of a system font set. Characters the console takes from the set's other files (Hangul, Thai and Arabic in Japanese and Chinese sets, Arabic in European sets) are reported as unsupported glyphs. The console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans CJK) is loaded instead. It is not the system font: its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [Font writing lines](../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontCreateWritingLine`, `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics` and `sceFontWritingLineClear` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libFont.cpp): runs are placed left to right, each at the line's advance so far, with no spacing or adjustment. Only horizontal and left-to-right lines, writing attribute 0 and non-null run metrics are modelled; other values, the metrics of an empty line and unknown or null handles throw, since no reference gives their result or error code. `sceFontWritingLineGetOrderingSpace` still throws (KytyPS5's values are placeholders). Lines are allocated on the host, not in the font memory - [sceImeKeyboardGetResourceId](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) reports that no USB keyboard is connected (`0x80bc0004`, as [shadPS4](https://github.com/shadps4-emu/shadPS4) does) because IME keyboard events are not delivered to the handler, although [libSceKeyboard](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) exposes the host keyboard -- [scePadRead](../../core/libs/prx/libScePad/Export.cpp) (libScePad) keeps no queue of past states: each call writes the current state to the first entry and returns 1 whatever `num` is, so input that changes and changes back between two calls is not reported From 71e0f6428f91d8c025631e856e9df332648cccad Mon Sep 17 00:00:00 2001 From: TideOPL Date: Wed, 7 Oct 2026 11:06:09 +0100 Subject: [PATCH 4/6] style(libScePad): remove code comments Per CONVENTIONS, code comments only mark technical debt. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libScePad/include/PadState.hpp | 1 - core/libs/prx/libScePad/src/PadState.cpp | 2 -- core/libs/tests/GuestProcessTime.cpp | 1 - 3 files changed, 4 deletions(-) diff --git a/core/libs/prx/libScePad/include/PadState.hpp b/core/libs/prx/libScePad/include/PadState.hpp index 1d2205fa6..8100e33c5 100644 --- a/core/libs/prx/libScePad/include/PadState.hpp +++ b/core/libs/prx/libScePad/include/PadState.hpp @@ -51,7 +51,6 @@ struct PadOutputState { namespace Pad { void Initialize(); PadData ReadState(); -// Drains up to `num` queued states, oldest first; with none queued, writes the current state. Returns the count written. int Read(PadData* data, int num); void SetVibration(std::uint8_t large, std::uint8_t small); void SetVibrationMode(int mode); diff --git a/core/libs/prx/libScePad/src/PadState.cpp b/core/libs/prx/libScePad/src/PadState.cpp index 3da6c4f21..14ce43eac 100644 --- a/core/libs/prx/libScePad/src/PadState.cpp +++ b/core/libs/prx/libScePad/src/PadState.cpp @@ -18,7 +18,6 @@ namespace { PadInputState input; std::uint64_t timestamp = 0; }; - // States published since the last scePadRead; the oldest is dropped once 64 are waiting. constexpr std::size_t kQueueCapacity = 64; std::deque queue; std::exception_ptr failure; @@ -108,7 +107,6 @@ void Pad::Initialize() { } namespace { -// Converts one input sample; motion fusion integrates up to `now` and touch ids advance per call. PadData BuildData(const PadInputState& state, std::uint64_t changeTime, std::uint64_t now) { PadData data{}; data.buttons = state.buttons; diff --git a/core/libs/tests/GuestProcessTime.cpp b/core/libs/tests/GuestProcessTime.cpp index cb8a6186e..b24cf8249 100644 --- a/core/libs/tests/GuestProcessTime.cpp +++ b/core/libs/tests/GuestProcessTime.cpp @@ -11,7 +11,6 @@ static void Require(bool condition, const char* message) { } } -// The first call in a process also fixes the process start, so it must not read the clock before it. int main(int argc, char** argv) { constexpr std::uint64_t minuteMicros = 60ULL * 1000 * 1000; if (argc > 1 && std::strcmp(argv[1], "counter") == 0) { From 8b28f53a4dcf11cdf0bee7b673f9833e5263e4d2 Mon Sep 17 00:00:00 2001 From: TideOPL Date: Thu, 8 Oct 2026 11:00:22 +0100 Subject: [PATCH 5/6] revert(libkernel): move the process time fix to its own pull request --- core/libs/prx/libkernel/CMakeLists.txt | 10 ---------- core/libs/prx/libkernel/Time/Time.cpp | 6 ++---- core/libs/tests/GuestProcessTime.cpp | 24 ------------------------ 3 files changed, 2 insertions(+), 38 deletions(-) delete mode 100644 core/libs/tests/GuestProcessTime.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 752f3f7e8..31ad9ae4b 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -84,16 +84,6 @@ if(BUILD_TESTING) set_tests_properties(guest_posix_sem_init guest_posix_sem_post PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() - add_test_executable(guest_process_time_tests ${LIBS_INCLUDE_DIR}/tests/GuestProcessTime.cpp) - target_include_directories(guest_process_time_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_process_time_tests PRIVATE libkernel libc) - configure_windows_unwind(guest_process_time_tests) - add_test(NAME guest_process_time COMMAND guest_process_time_tests) - add_test(NAME guest_process_time_counter COMMAND guest_process_time_tests counter) - if(WIN32) - set_tests_properties(guest_process_time guest_process_time_counter PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") - endif() add_library(guest_module_fixture SHARED ${LIBS_INCLUDE_DIR}/tests/GuestModuleFixture.cpp) target_include_directories(guest_module_fixture PRIVATE ${LIBS_INCLUDE_DIR}) add_custom_command(TARGET guest_module_fixture POST_BUILD diff --git a/core/libs/prx/libkernel/Time/Time.cpp b/core/libs/prx/libkernel/Time/Time.cpp index 521328a18..395f85829 100644 --- a/core/libs/prx/libkernel/Time/Time.cpp +++ b/core/libs/prx/libkernel/Time/Time.cpp @@ -147,13 +147,11 @@ static const bool g_timerSelfTest = [] { extern "C" { std::uint64_t APS5_VABI sceKernelGetProcessTime() { - const std::uint64_t start = GetStartNanos(); - return (GetMonotonicNanos() - start) / 1000ULL; + return (GetMonotonicNanos() - GetStartNanos()) / 1000ULL; } std::uint64_t APS5_VABI sceKernelGetProcessTimeCounter() { - const std::uint64_t start = GetStartNanos(); - return GetMonotonicNanos() - start; + return GetMonotonicNanos() - GetStartNanos(); } std::uint64_t APS5_VABI sceKernelGetProcessTimeCounterFrequency() { diff --git a/core/libs/tests/GuestProcessTime.cpp b/core/libs/tests/GuestProcessTime.cpp deleted file mode 100644 index b24cf8249..000000000 --- a/core/libs/tests/GuestProcessTime.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "prx/libkernel/Time/include/Time.hpp" -#include -#include -#include -#include - -static void Require(bool condition, const char* message) { - if (!condition) { - std::fprintf(stderr, "%s\n", message); - std::abort(); - } -} - -int main(int argc, char** argv) { - constexpr std::uint64_t minuteMicros = 60ULL * 1000 * 1000; - if (argc > 1 && std::strcmp(argv[1], "counter") == 0) { - Require(sceKernelGetProcessTimeCounter() < minuteMicros * 1000, "first process time counter wrapped"); - return 0; - } - const std::uint64_t first = sceKernelGetProcessTime(); - Require(first < minuteMicros, "first process time wrapped"); - Require(sceKernelGetProcessTime() >= first, "process time went backwards"); - return 0; -} From 39463913c70f0ac7aff7c486662be20510ef69c6 Mon Sep 17 00:00:00 2001 From: TideOPL Date: Thu, 8 Oct 2026 21:56:33 +0100 Subject: [PATCH 6/6] fix(libScePad): fuse queued pad states at the read time Keeping lastFuseTime at its maximum never let it recover from a process time that wrapped on the first call (fixed separately in #1501), so tilt correction stopped and guest_pad failed. Queued states keep their own timestamp; the orientation is fused once at the time of the read, as scePadReadState does. --- core/libs/prx/libScePad/src/PadState.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libScePad/src/PadState.cpp b/core/libs/prx/libScePad/src/PadState.cpp index 14ce43eac..1bd213078 100644 --- a/core/libs/prx/libScePad/src/PadState.cpp +++ b/core/libs/prx/libScePad/src/PadState.cpp @@ -129,7 +129,7 @@ PadData BuildData(const PadInputState& state, std::uint64_t changeTime, std::uin orientation = Quat{}; biasIntegral[0] = biasIntegral[1] = biasIntegral[2] = 0.0f; } - lastFuseTime = std::max(lastFuseTime, now); + lastFuseTime = now; data.orientation_x = orientation.x; data.orientation_y = orientation.y; data.orientation_z = orientation.z; @@ -183,14 +183,15 @@ int Pad::Read(PadData* data, int num) { std::lock_guard lock(stateMutex); if (failure) std::rethrow_exception(failure); if (!initialized) throw std::runtime_error("Pad: read before initialization"); + const std::uint64_t now = sceKernelGetProcessTime(); if (queue.empty()) { - data[0] = BuildData(state, timestamp, sceKernelGetProcessTime()); + data[0] = BuildData(state, timestamp, now); return 1; } int count = 0; while (count < num && !queue.empty()) { const QueuedInput& entry = queue.front(); - data[count++] = BuildData(entry.input, entry.timestamp, entry.timestamp); + data[count++] = BuildData(entry.input, entry.timestamp, now); queue.pop_front(); } return count;