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.
This commit is contained in:
Luke Street
2026-04-11 17:50:52 -06:00
committed by GitHub
parent b628a1beb6
commit aafd50cd09
4 changed files with 46 additions and 33 deletions
+3 -5
View File
@@ -15,12 +15,10 @@
#include <Windows.h>
#include <shellapi.h>
#include <intrin.h>
#else
#include "SDL3/SDL_timer.h"
#endif
#include "dusk/logging.h"
constexpr auto DUSK_FRAME_PERIOD = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(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
};
+41 -27
View File
@@ -1,26 +1,30 @@
#include "JSystem/JSystem.h" // IWYU pragma: keep
#include <dolphin/gx.h>
#include <dolphin/vi.h>
#include <gx.h>
#include <stdint.h>
#include <vi.h>
#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 <gx.h>
#include <vi.h>
#include "global.h"
#include <stdint.h>
#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 <chrono>
#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::nanoseconds>(
std::chrono::duration<double>(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<float>(sleepTime.count()) / static_cast<float>(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<Uint64>(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<Uint64>((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> 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<JFWAlarm*>(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;
+2
View File
@@ -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) {
-1
View File
@@ -53,7 +53,6 @@
#include "dusk/main.h"
#include "dusk/imgui/ImGuiConsole.hpp"
#include "version.h"
#include "dusk/time.h"
#include <aurora/aurora.h>
#include <aurora/event.h>