From aafd50cd0906217b5fbe6339ca5219e74ee4fb3f Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 11 Apr 2026 17:50:52 -0600 Subject: [PATCH] Use Limiter class in waitForTick (#330) This uses the existing `Limiter` class (stolen from Metaforce) in `JFWDisplay::waitForTick`. The limiter also now uses `SDL_DelayPrecise` internally on non-Windows platforms. On Windows, the existing `NanoSleep` logic is untouched, as it appears to provide a more stable framerate for the folks testing it on Windows than `SDL_DelayPrecise` does. On Linux, however, `SDL_DelayPrecise` is plenty accurate. --- include/dusk/time.h | 8 +-- libs/JSystem/src/JFramework/JFWDisplay.cpp | 68 +++++++++++++--------- libs/JSystem/src/JUtility/JUTVideo.cpp | 2 + src/m_Do/m_Do_main.cpp | 1 - 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/include/dusk/time.h b/include/dusk/time.h index 94f819f76b..948a2fc171 100644 --- a/include/dusk/time.h +++ b/include/dusk/time.h @@ -15,12 +15,10 @@ #include #include #include +#else +#include "SDL3/SDL_timer.h" #endif -#include "dusk/logging.h" - -constexpr auto DUSK_FRAME_PERIOD = std::chrono::duration_cast(std::chrono::duration(1001.0 / 30000.0)); - class Limiter { using delta_clock = std::chrono::high_resolution_clock; using duration_t = std::chrono::nanoseconds; @@ -101,7 +99,7 @@ private: } while (current.QuadPart - start.QuadPart < ticksToWait); } #else - void NanoSleep(const duration_t duration) { std::this_thread::sleep_for(duration); } + void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration.count()); } #endif }; diff --git a/libs/JSystem/src/JFramework/JFWDisplay.cpp b/libs/JSystem/src/JFramework/JFWDisplay.cpp index 0ae8e95fa7..d3e7abf0a1 100644 --- a/libs/JSystem/src/JFramework/JFWDisplay.cpp +++ b/libs/JSystem/src/JFramework/JFWDisplay.cpp @@ -1,26 +1,30 @@ #include "JSystem/JSystem.h" // IWYU pragma: keep -#include -#include -#include -#include -#include -#include "SDL3/SDL_timer.h" -#include "JSystem/J2DGraph/J2DOrthoGraph.h" #include "JSystem/JFramework/JFWDisplay.h" +#include "JSystem/J2DGraph/J2DOrthoGraph.h" #include "JSystem/JKernel/JKRHeap.h" #include "JSystem/JUtility/JUTAssert.h" #include "JSystem/JUtility/JUTConsole.h" #include "JSystem/JUtility/JUTDbPrint.h" #include "JSystem/JUtility/JUTProcBar.h" -#include "aurora/aurora.h" +#include +#include +#include "global.h" +#include + +#ifdef TARGET_PC #include "dusk/dusk.h" #include "dusk/gx_helper.h" #include "dusk/logging.h" #include "dusk/settings.h" -#include "global.h" +#include "dusk/time.h" + +#include "SDL3/SDL_timer.h" #include "tracy/Tracy.hpp" +#include +#endif + void JFWDisplay::ctor_subroutine(bool enableAlpha) { mEnableAlpha = enableAlpha; mClamp = GX_CLAMP_TOP | GX_CLAMP_BOTTOM; @@ -377,6 +381,19 @@ void JFWDisplay::waitBlanking(int param_0) { } } +#if TARGET_PC +constexpr auto FRAME_PERIOD = std::chrono::duration_cast( + std::chrono::duration(1001.0 / 30000.0)); +constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2; + +static void waitPrecise(Limiter& limiter, Uint64 targetNs) { + const auto sleepTime = limiter.SleepTime(std::chrono::nanoseconds(targetNs)); + dusk::frameUsagePct = + 100.0f * (1.0f - static_cast(sleepTime.count()) / static_cast(targetNs)); + limiter.Sleep(std::chrono::nanoseconds(targetNs)); +} +#endif + static void waitForTick(u32 p1, u16 p2) { #if TARGET_PC if (dusk::getSettings().game.enableFrameInterpolation) { @@ -385,44 +402,42 @@ static void waitForTick(u32 p1, u16 p2) { if (dusk::getTransientSettings().skipFrameRateLimit) { p1 = OS_TIMER_CLOCK / 120; } + ZoneScopedC(tracy::Color::DimGray); #endif - ZoneScopedC(tracy::Color::DimGray); - if (p1 != 0) - { + if (p1 != 0) { +#if TARGET_PC + static Limiter limiter; + waitPrecise(limiter, static_cast(OSTicksToMicroseconds(p1)) * 1000ULL); +#else static OSTime nextTick = OSGetTime(); OSTime time = OSGetTime(); - OSTime waitTime = (nextTick > time) ? (nextTick - time) : 0; while (time < nextTick) { JFWDisplay::getManager()->threadSleep((nextTick - time)); time = OSGetTime(); } - dusk::frameUsagePct = 100.0f * (1.0f - (float)waitTime / (float)p1); nextTick = time + p1; +#endif } else { - static u32 nextCount = VIGetRetraceCount(); u32 uVar1 = (p2 == 0) ? 1 : p2; +#if TARGET_PC + static Limiter limiter; + waitPrecise(limiter, static_cast((RETRACE_PERIOD * uVar1).count())); +#else + static u32 nextCount = VIGetRetraceCount(); OSMessage msg; do { if (!OSReceiveMessage(JUTVideo::getManager()->getMessageQueue(), &msg, - OS_MESSAGE_BLOCK)) - { + OS_MESSAGE_BLOCK)) { msg = 0; } } while (((intptr_t)msg - (intptr_t)nextCount) < 0); - dusk::frameUsagePct = 100.0f; nextCount = (intptr_t)msg + uVar1; +#endif } } JSUList JFWAlarm::sList(false); - -#if TARGET_PC -void JFWDisplay::threadSleep(s64 time) { - SDL_DelayNS(OSTicksToMicroseconds(time) * 1'000); -} -#else - static void JFWThreadAlarmHandler(OSAlarm* p_alarm, OSContext* p_ctx) { JFWAlarm* alarm = static_cast(p_alarm); alarm->removeLink(); @@ -440,7 +455,6 @@ void JFWDisplay::threadSleep(s64 time) { OSSuspendThread(alarm.getThread()); OSRestoreInterrupts(status); } -#endif static void dummy() { JUTXfb::getManager()->setDisplayingXfbIndex(0); @@ -480,7 +494,7 @@ void JFWDisplay::clearEfb(GXColor color) { void JFWDisplay::clearEfb(int param_0, int param_1, int param_2, int param_3, GXColor color) { STUB_RET(); - + u16 width; u16 height; Mtx44 mtx; diff --git a/libs/JSystem/src/JUtility/JUTVideo.cpp b/libs/JSystem/src/JUtility/JUTVideo.cpp index bb1f2cee3b..f718fcac23 100644 --- a/libs/JSystem/src/JUtility/JUTVideo.cpp +++ b/libs/JSystem/src/JUtility/JUTVideo.cpp @@ -182,7 +182,9 @@ void JUTVideo::postRetraceProc(u32 retrace_count) { sManager->mPostCallback(retrace_count); } +#ifndef TARGET_PC // Not read by JFWDisplay waitForTick OSSendMessage(&sManager->mMessageQueue, (OSMessage)(uintptr_t)VIGetRetraceCount(), OS_MESSAGE_NOBLOCK); +#endif } void JUTVideo::setRenderMode(GXRenderModeObj const* pObj) { diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 1be3a06b44..4dd1a658e5 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -53,7 +53,6 @@ #include "dusk/main.h" #include "dusk/imgui/ImGuiConsole.hpp" #include "version.h" -#include "dusk/time.h" #include #include