From ef511d588970ac03c07b146dce723a85f1fd7e87 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 12 Nov 2023 15:48:41 +0100 Subject: [PATCH 01/11] port: fix #231 --- src/game/lv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/lv.c b/src/game/lv.c index c2c211396..a45954107 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -2050,7 +2050,7 @@ void lvTick(void) g_Vars.lvupdate240 = 0; for (j = 0; j < PLAYERCOUNT(); j++) { - g_Vars.players[j]->joybutinhibit = 0xefffffff; + g_Vars.players[j]->joybutinhibit = 0xffffefff; } } else { s32 slowmo = lvGetSlowMotionType(); From 28b172f5c7beee5c65ddbe2110365846f86717c1 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 12 Nov 2023 16:09:58 +0100 Subject: [PATCH 02/11] port: make autoguns fire only on even frames at 60+ fps --- src/game/propobj.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/game/propobj.c b/src/game/propobj.c index 604e6efc3..2f111e6d0 100644 --- a/src/game/propobj.c +++ b/src/game/propobj.c @@ -9002,6 +9002,12 @@ void autogunInitMatrices(struct prop *prop, Mtxf *mtx) void autogunTickShoot(struct prop *autogunprop) { +#ifndef PLATFORM_N64 + // HACK: fire every other tick at 60+ fps and every tick at <60 fps + if (g_Vars.lvupdate240 <= TICKS(4) && (g_Vars.lvframe60 & 1)) { + return; + } +#endif if (!lvIsPaused()) { struct autogunobj *autogun = (struct autogunobj *) autogunprop->obj; struct defaultobj *obj = autogunprop->obj; From 8a348ab1b5a9ef2e9b86be3a81827e11d638a67f Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 13 Nov 2023 21:07:53 +0100 Subject: [PATCH 03/11] port: fix bullet casings going behind the world --- src/game/bondgun.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/game/bondgun.c b/src/game/bondgun.c index c7b3b4048..efe979624 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -10918,6 +10918,7 @@ void bgunRender(Gfx **gdlptr) struct modelrenderdata renderdata = {NULL, true, 3}; // 10c struct player *player; s32 i; + const u32 wnorm = mtx00016dcc(0, 300); static bool renderhand = true; // var800702dc @@ -10983,7 +10984,7 @@ void bgunRender(Gfx **gdlptr) gSPLookAt(gdl++, camGetLookAt()); } - gSPPerspNormalize(gdl++, mtx00016dcc(0, 300)); + gSPPerspNormalize(gdl++, wnorm); // There is support for guns having a TV screen on them // but no guns have this model part so it's not used. @@ -11158,7 +11159,17 @@ void bgunRender(Gfx **gdlptr) } } +#ifndef PLATFORM_N64 + // put the casings into the same Z range as the gun + gSPPerspNormalize(gdl++, wnorm); +#endif + casingsRender(&gdl); + +#ifndef PLATFORM_N64 + gSPPerspNormalize(gdl++, viGetPerspScale()); +#endif + zbufSwap(); gdl = zbufConfigureRdp(gdl); From 72e9e2af95e40ebf87183b52720c2ecb41c71eb2 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 14 Nov 2023 23:29:44 +0100 Subject: [PATCH 04/11] port: very experimental uncapped framerate support set Game.TickRateDivisor in pd.ini to 0 this still has many issues, such as combat boost not working --- port/src/main.c | 3 +++ port/src/pdmain.c | 2 +- src/game/timing.c | 6 +++++- src/include/data.h | 1 + src/lib/varsinit.c | 4 ++++ 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/port/src/main.c b/port/src/main.c index 7e6d059f4..d13024b08 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -35,6 +35,8 @@ u32 g_VmNumPageMisses = 0; u32 g_VmNumPageReplaces = 0; u8 g_VmShowStats = 0; +s32 g_TickRateDiv = 1; + extern s32 g_StageNum; s32 bootGetMemSize(void) @@ -135,6 +137,7 @@ PD_CONSTRUCTOR static void gameConfigInit(void) configRegisterInt("Game.MemorySize", &g_OsMemSizeMb, 4, 2048); configRegisterInt("Game.CenterHUD", &g_HudCenter, 0, 1); configRegisterFloat("Game.ScreenShakeIntensity", &g_ViShakeIntensityMult, 0.f, 10.f); + configRegisterInt("Game.TickRateDivisor", &g_TickRateDiv, 0, 10); for (s32 j = 0; j < MAX_PLAYERS; ++j) { const s32 i = j + 1; configRegisterFloat(strFmt("Game.Player%d.FovY", i), &g_PlayerExtCfg[j].fovy, 5.f, 175.f); diff --git a/port/src/pdmain.c b/port/src/pdmain.c index 9ccb3f70a..77c031800 100644 --- a/port/src/pdmain.c +++ b/port/src/pdmain.c @@ -494,7 +494,7 @@ void mainLoop(void) while (g_MainChangeToStageNum < 0) { const s32 cycles = osGetCount() - g_Vars.thisframestartt; - if (cycles >= g_Vars.mininc60 * CYCLES_PER_FRAME - CYCLES_PER_FRAME / 2) { + if (!g_Vars.mininc60 || (cycles >= g_Vars.mininc60 * CYCLES_PER_FRAME - CYCLES_PER_FRAME / 2)) { schedStartFrame(&g_Sched); mainTick(); schedEndFrame(&g_Sched); diff --git a/src/game/timing.c b/src/game/timing.c index 3739af362..9a7c8e380 100644 --- a/src/game/timing.c +++ b/src/game/timing.c @@ -41,12 +41,16 @@ void frametimeCalculate(void) diffframe60 = (g_Vars.lostframetime60t + diffframet + CYCLES_PER_FRAME / 2) / CYCLES_PER_FRAME; diffframe240 = (g_Vars.lostframetime240t + diffframet + CYCLES_PER_FRAME / 2 / 4) / (CYCLES_PER_FRAME / 4); - } while (diffframe60 < g_Vars.mininc60); + } while (g_Vars.mininc60 && diffframe60 < g_Vars.mininc60); g_Vars.lostframetime60t = g_Vars.lostframetime60t + diffframet - diffframe60 * CYCLES_PER_FRAME; g_Vars.lostframetime240t = g_Vars.lostframetime240t + diffframet - diffframe240 * (CYCLES_PER_FRAME / 4); +#ifdef PLATFORM_N64 g_Vars.mininc60 = 1; +#else + g_Vars.mininc60 = g_TickRateDiv; +#endif frametimeApply(diffframe60, diffframe240, count); } diff --git a/src/include/data.h b/src/include/data.h index 5a897577d..e94aaa7c5 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -545,6 +545,7 @@ extern u32 g_HudAlignModeR; extern s32 g_PrevFrameFb; extern s32 g_BlurFb; extern s32 g_BlurFbCapTimer; +extern s32 g_TickRateDiv; #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) diff --git a/src/lib/varsinit.c b/src/lib/varsinit.c index 0d7925fe7..c64418044 100644 --- a/src/lib/varsinit.c +++ b/src/lib/varsinit.c @@ -28,7 +28,11 @@ void varsInit(void) g_Vars.lostframetime60t = 0; g_Vars.lostframetime240t = 0; g_Vars.lvupdate240rem = 2; +#ifdef PLATFORM_N64 g_Vars.mininc60 = 1; +#else + g_Vars.mininc60 = g_TickRateDiv; +#endif g_Vars.roomportalrecursionlimit = 254; g_Vars.diffframe240f = 4; g_Vars.diffframe240freal = 4; From 32b2e9c28e50fe3bce0736b5ef9aa41d00c1176a Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 14 Nov 2023 23:58:40 +0100 Subject: [PATCH 05/11] port: fix sliding doors exploding at non-60fps framerates --- src/game/propobj.c | 9 +++++++++ src/include/types.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/game/propobj.c b/src/game/propobj.c index 2f111e6d0..3ba8b6dab 100644 --- a/src/game/propobj.c +++ b/src/game/propobj.c @@ -7671,7 +7671,12 @@ void doorTick(struct prop *doorprop) } // Update frac +#ifdef PLATFORM_N64 if (door->lastcalc60 < g_Vars.lvframe60 || g_Vars.lvupdate240 == 0) { +#else + // lastcalc60 actually stores lvframe240 + if (door->lastcalc60 < g_Vars.lvframe240 || g_Vars.lvupdate240 == 0) { +#endif doorsCalcFrac(door); } @@ -20333,7 +20338,11 @@ void doorsCalcFrac(struct doorobj *door) func0f08d460(loopdoor); } +#ifdef PLATFORM_N64 loopdoor->lastcalc60 = g_Vars.lvframe60; +#else + loopdoor->lastcalc60 = g_Vars.lvframe240; // actually use the 240hz counter for higher framerates +#endif loopdoor = loopdoor->sibling; if (loopdoor == door) { diff --git a/src/include/types.h b/src/include/types.h index 9917f21f7..171b0eda7 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -1523,7 +1523,7 @@ struct doorobj { // objtype 0x01 /*0xc4*/ s16 portalnum; /*0xc6*/ s8 soundtype; /*0xc7*/ s8 fadetime60; // counts down - /*0xc8*/ s32 lastcalc60; + /*0xc8*/ s32 lastcalc60; // port: actually stores 240hz frame number /*0xcc*/ u8 laserfade; /*0xcd*/ u8 unusedmaybe[3]; /*0xd0*/ u8 shadeinfo1[4]; // player 1 From 9c7f348652c960b6d2fd46a0e485c98edb024d40 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 15 Nov 2023 00:24:38 +0100 Subject: [PATCH 06/11] port: use the 240hz timer in activemenu --- src/game/activemenutick.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/activemenutick.c b/src/game/activemenutick.c index 29d09993b..39b66f02c 100644 --- a/src/game/activemenutick.c +++ b/src/game/activemenutick.c @@ -411,7 +411,7 @@ void amTick(void) // Update alpha of slots so they fade in if (g_AmMenus[g_AmIndex].alphafrac < 1) { - g_AmMenus[g_AmIndex].alphafrac += LVUPDATE60FREAL() / 30.0f; + g_AmMenus[g_AmIndex].alphafrac += (f32)g_Vars.lvupdate240 / (4.f * 30.0f); } if (g_AmMenus[g_AmIndex].alphafrac > 1) { @@ -419,7 +419,7 @@ void amTick(void) } // Make selection border pulsate - g_AmMenus[g_AmIndex].selpulse += LVUPDATE60FREAL() / 5.0f; + g_AmMenus[g_AmIndex].selpulse += (f32)g_Vars.lvupdate240 / (4.f * 5.0f); if (g_AmMenus[g_AmIndex].selpulse > 18.849555969238f) { g_AmMenus[g_AmIndex].selpulse -= 18.849555969238f; From f394f209b2d73fd24f417c9e7bb27f84ed1d777f Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 15 Nov 2023 00:25:01 +0100 Subject: [PATCH 07/11] port: fix npc footsteps at high framerates --- src/game/footstep.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/game/footstep.c b/src/game/footstep.c index ddbcc3546..2480ad470 100644 --- a/src/game/footstep.c +++ b/src/game/footstep.c @@ -177,9 +177,10 @@ void footstepCheckDefault(struct chrdata *chr) chr->footstep = 2; } } else { -#ifndef PLATFORM_N64 // fix spamming footsteps for 60fps port - if (g_Vars.lvupdate240 == LV_SLOMO_TICK_CAP) { - s32 roundedframe = (s32)(frame*2); +#ifndef PLATFORM_N64 + // fix spamming footsteps at >= 60fps + if (g_Vars.lvupdate240 < 4) { + const s32 roundedframe = (s32)(frame * (4.f / (f32)g_Vars.lvupdate240)); if (roundedframe == (g_FootstepAnims[i].frame1*2) && prevframe < g_FootstepAnims[i].frame1) { chr->footstep = 1; } else if (roundedframe == (g_FootstepAnims[i].frame2*2) && prevframe < g_FootstepAnims[i].frame2) { From 5bb4f1d193ce042f5258407abeca5db064c979aa Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 15 Nov 2023 00:25:10 +0100 Subject: [PATCH 08/11] port: fix (?) slomo at high framerates --- src/game/lv.c | 8 ++++---- src/include/game/lv.h | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/game/lv.c b/src/game/lv.c index a45954107..e100aeda9 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -2059,7 +2059,7 @@ void lvTick(void) if (slowmo == SLOWMOTION_ON) { if (g_Vars.speedpillon == false || g_Vars.in_cutscene) { if (g_Vars.lvupdate240 > LV_SLOMO_TICK_CAP) { - g_Vars.lvupdate240 = LV_SLOMO_TICK_CAP; + g_Vars.lvupdate240 = LV_SLOMO_TICK_RATE; } } } else if (slowmo == SLOWMOTION_SMART) { @@ -2090,7 +2090,7 @@ void lvTick(void) if (foundnearbychr) { if (g_Vars.lvupdate240 > LV_SLOMO_TICK_CAP) { - g_Vars.lvupdate240 = LV_SLOMO_TICK_CAP; + g_Vars.lvupdate240 = LV_SLOMO_TICK_RATE; } } else { if (g_Vars.lvupdate240 > TICKS(8)) { @@ -2099,7 +2099,7 @@ void lvTick(void) } } else { if (g_Vars.lvupdate240 > LV_SLOMO_TICK_CAP) { - g_Vars.lvupdate240 = LV_SLOMO_TICK_CAP; + g_Vars.lvupdate240 = LV_SLOMO_TICK_RATE; } } } @@ -2107,7 +2107,7 @@ void lvTick(void) // Slow motion settings are off if (g_Vars.speedpillon && g_Vars.in_cutscene == false) { if (g_Vars.lvupdate240 > LV_SLOMO_TICK_CAP) { - g_Vars.lvupdate240 = LV_SLOMO_TICK_CAP; + g_Vars.lvupdate240 = LV_SLOMO_TICK_RATE; } } } diff --git a/src/include/game/lv.h b/src/include/game/lv.h index 961af91d0..f4f6dc808 100644 --- a/src/include/game/lv.h +++ b/src/include/game/lv.h @@ -6,10 +6,12 @@ #ifdef PLATFORM_N64 // game runs at ~30, so slomo = 1/2 of 30fps -#define LV_SLOMO_TICK_CAP TICKS(4) +#define LV_SLOMO_TICK_CAP 4 +#define LV_SLOMO_TICK_RATE LV_SLOMO_TICK_CAP #else -// game runs at 60+, so slomo = 1/2 of 60fps -#define LV_SLOMO_TICK_CAP TICKS(2) +// game runs at 60+, so slomo = 1/2 of whatever framerate we're running at +#define LV_SLOMO_TICK_CAP 1 // capping at >= 240fps would make it explode +#define LV_SLOMO_TICK_RATE (g_Vars.lvupdate240 / 2) #endif u32 getVar80084040(void); From 11e318b70fe9c0f9691c92e506c8e5094c5acf2d Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 15 Nov 2023 10:32:54 +0100 Subject: [PATCH 09/11] port: mouselook speed now independent of framerate --- src/game/bondeyespy.c | 2 -- src/game/bondmove.c | 5 +++-- src/game/player.c | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/game/bondeyespy.c b/src/game/bondeyespy.c index bd9439e0c..fe281cc84 100644 --- a/src/game/bondeyespy.c +++ b/src/game/bondeyespy.c @@ -922,8 +922,6 @@ void eyespyProcessInput(bool allowbuttons) f32 mdx, mdy; inputMouseGetScaledDelta(&mdx, &mdy); if (mdx || mdy) { - mdx *= g_Vars.lvupdate60freal; - mdy *= g_Vars.lvupdate60freal; if (g_Vars.currentplayerstats && !optionsGetForwardPitch(g_Vars.currentplayerstats->mpindex)) { mdy = -mdy; } diff --git a/src/game/bondmove.c b/src/game/bondmove.c index 1a74a6fd2..dcbc70df6 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -714,6 +714,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i #ifndef PLATFORM_N64 const bool allowmlook = (g_Vars.currentplayernum == 0) && (allowc1x || allowc1y); bool allowmcross = false; + const f32 mlookscale = g_Vars.lvupdate240 ? (4.f / (f32)g_Vars.lvupdate240) : 0.25f; #endif controlmode = optionsGetControlMode(g_Vars.currentplayerstats->mpindex); @@ -2017,7 +2018,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i } #ifndef PLATFORM_N64 - fVar25 += movedata.freelookdy; + fVar25 += movedata.freelookdy * mlookscale; #endif g_Vars.currentplayer->speedverta = -fVar25 * tmp; @@ -2058,7 +2059,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i } #ifndef PLATFORM_N64 - fVar25 += movedata.freelookdx; + fVar25 += movedata.freelookdx * mlookscale; #endif g_Vars.currentplayer->speedthetacontrol = fVar25 * tmp; diff --git a/src/game/player.c b/src/game/player.c index 8bb27558f..2c8dd90b6 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -3592,8 +3592,8 @@ void playerTick(bool arg0) if (g_Vars.currentplayerstats && !optionsGetForwardPitch(g_Vars.currentplayerstats->mpindex)) { mdy = -mdy; } - sp178 += mdy * LVUPDATE60FREAL() * 0.00025f; - sp174 -= mdx * LVUPDATE60FREAL() * 0.00025f; + sp178 += mdy * 0.00025f; + sp174 -= mdx * 0.00025f; } } #endif From 4a16f27abde029a601a7ce976b1078dff394b69a Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 15 Nov 2023 12:13:13 +0100 Subject: [PATCH 10/11] port: don't slow down the mouse at exactly 240 fps --- src/game/bondmove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/bondmove.c b/src/game/bondmove.c index dcbc70df6..45f75ea6c 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -714,7 +714,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i #ifndef PLATFORM_N64 const bool allowmlook = (g_Vars.currentplayernum == 0) && (allowc1x || allowc1y); bool allowmcross = false; - const f32 mlookscale = g_Vars.lvupdate240 ? (4.f / (f32)g_Vars.lvupdate240) : 0.25f; + const f32 mlookscale = g_Vars.lvupdate240 ? (4.f / (f32)g_Vars.lvupdate240) : 4.f; #endif controlmode = optionsGetControlMode(g_Vars.currentplayerstats->mpindex); From 6f55843f3078da3be440a1c9ed6c7feeedaa6c89 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 15 Nov 2023 13:53:18 +0100 Subject: [PATCH 11/11] port: backport better SDL framepacing from LUS --- port/fast3d/gfx_sdl2.cpp | 77 +++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/port/fast3d/gfx_sdl2.cpp b/port/fast3d/gfx_sdl2.cpp index d6eefb1c6..274bf1230 100644 --- a/port/fast3d/gfx_sdl2.cpp +++ b/port/fast3d/gfx_sdl2.cpp @@ -1,19 +1,31 @@ #include #include -#ifdef __MINGW32__ -// mingw has its nanosleep implementation in pthread.h for some reson -#include -#else #include #include -#endif +#include "platform.h" #include "system.h" #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" -#define GFX_BACKEND_NAME "SDL" +// define a way to yield the cpu when in a busy wait +#ifdef PLATFORM_WIN32 +// winapi provides a yield macro +#include +#define DO_YIELD() YieldProcessor() +#elif defined(PLATFORM_X86) || defined(PLATFORM_X86_64) +// this should work even if the code is not built with SSE enabled, at least on gcc and clang, +// but if it doesn't we'll have to use __builtin_ia32_pause() or something +#include +#define DO_YIELD() _mm_pause() +#elif defined(PLATFORM_ARM) +// same as YieldProcessor() on ARM Windows +#define DO_YIELD() __asm__ volatile("dmb ishst\n\tyield":::"memory") +#else +// fuck it +#define DO_YIELD() do { } while (0) +#endif static SDL_Window* wnd; static SDL_GLContext ctx; @@ -27,15 +39,32 @@ static bool fullscreen_state; static bool is_running = true; static void (*on_fullscreen_changed_callback)(bool is_now_fullscreen); -static uint64_t previous_time; - static int target_fps = 120; // above 60 since vsync is enabled by default - +static uint64_t previous_time; static uint64_t qpc_freq; +#ifdef PLATFORM_WIN32 +// on win32 we use waitable timers instead of nanosleep +typedef HANDLE WINAPI (*CREATEWAITABLETIMEREXAFN)(LPSECURITY_ATTRIBUTES, LPCSTR, DWORD, DWORD); +static HANDLE timer; +static CREATEWAITABLETIMEREXAFN pfnCreateWaitableTimerExA; +#endif + #define FRAME_INTERVAL_US_NUMERATOR 1000000 #define FRAME_INTERVAL_US_DENOMINATOR (target_fps) +static inline void do_sleep(const int64_t left) { +#ifdef PLATFORM_WIN32 + static LARGE_INTEGER li; + li.QuadPart = -left; + SetWaitableTimer(timer, &li, 0, nullptr, nullptr, false); + WaitForSingleObject(timer, INFINITE); +#else + const timespec spec = { 0, left * 100 }; + nanosleep(&spec, nullptr); +#endif +} + static void set_fullscreen(bool on, bool call_callback) { if (fullscreen_state == on) { return; @@ -55,11 +84,6 @@ static void gfx_sdl_get_active_window_refresh_rate(uint32_t* refresh_rate) { *refresh_rate = mode.refresh_rate; } -static inline void do_sleep(const uint64_t ns) { - const timespec spec = { 0, ns }; - nanosleep(&spec, nullptr); -} - static void gfx_sdl_init(const char* game_name, const char* gfx_api_name, bool start_in_fullscreen, uint32_t width, uint32_t height, int32_t posX, int32_t posY) { window_width = width; @@ -143,6 +167,20 @@ static void gfx_sdl_init(const char* game_name, const char* gfx_api_name, bool s qpc_freq = SDL_GetPerformanceFrequency(); +#ifdef PLATFORM_WIN32 + // this function is only present on Vista+, so try to import it from kernel32 by hand + pfnCreateWaitableTimerExA = (CREATEWAITABLETIMEREXAFN)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateWaitableTimerExA"); + if (pfnCreateWaitableTimerExA) { + // function exists, try to create a hires timer + timer = pfnCreateWaitableTimerExA(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + } + if (!timer) { + // no function or hires timers not supported, fallback to lower resolution timer + sysLogPrintf(LOG_WARNING, "SDL: hires waitable timers not available"); + timer = CreateWaitableTimerA(nullptr, false, nullptr); + } +#endif + set_fullscreen(start_in_fullscreen, false); } @@ -211,11 +249,18 @@ static inline void sync_framerate_with_timer(void) { t = qpc_to_100ns(SDL_GetPerformanceCounter()); const int64_t next = previous_time + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR; - const int64_t left = next - t; + int64_t left = next - t; + // We want to exit a bit early, so we can busy-wait the rest to never miss the deadline + left -= 15000UL; if (left > 0) { - do_sleep(left * 100); + do_sleep(left); } + do { + DO_YIELD(); + t = qpc_to_100ns(SDL_GetPerformanceCounter()); + } while ((int64_t)t < next); + t = qpc_to_100ns(SDL_GetPerformanceCounter()); if (left > 0 && t - next < 10000) { // In case it takes some time for the application to wake up after sleep,