From 667cac98318d10f8bfc6ec3de76eaa991ece2798 Mon Sep 17 00:00:00 2001 From: MysterD Date: Wed, 18 Aug 2021 23:50:40 -0700 Subject: [PATCH] Rewrote the core of how the game determines how quickly it should run at Allows vsync to be enabled regardless of the monitor's refresh rate Automatically drops frames when it needs to The game no longer runs fast on 144hz monitors --- src/pc/gfx/gfx_dxgi.cpp | 43 ++++++++++++++++++++++++++++--- src/pc/gfx/gfx_sdl2.c | 56 +++++++++++++++++------------------------ src/pc/utils/misc.c | 4 +++ src/pc/utils/misc.h | 1 + 4 files changed, 67 insertions(+), 37 deletions(-) diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp index 5bdb73401..6358a48e4 100644 --- a/src/pc/gfx/gfx_dxgi.cpp +++ b/src/pc/gfx/gfx_dxgi.cpp @@ -42,6 +42,17 @@ #define FRAME_INTERVAL_US_DENOMINATOR 6 #endif +// TODO: figure out if this shit even works +#ifdef VERSION_EU +# define FRAMERATE 25 +#else +# define FRAMERATE 30 +#endif +// time between consequtive game frames +static const f64 sFrameTime = 1.0 / (2.0 * FRAMERATE); +static f64 sFrameTargetTime = 0; +extern "C" f64 clock_elapsed_f64(void); + using namespace Microsoft::WRL; // For ComPtr static bool inTextInput = false; @@ -395,7 +406,8 @@ static uint64_t qpc_to_us(uint64_t qpc) { } static bool gfx_dxgi_start_frame(void) { - DXGI_FRAME_STATISTICS stats; + // HACK: all of this is too confusing to bother with right now + /*DXGI_FRAME_STATISTICS stats; if (dxgi.swap_chain->GetFrameStatistics(&stats) == S_OK && (stats.SyncRefreshCount != 0 || stats.SyncQPCTime.QuadPart != 0ULL)) { { LARGE_INTEGER t0; @@ -507,13 +519,36 @@ static bool gfx_dxgi_start_frame(void) { dxgi.length_in_vsync_frames = vsyncs_to_wait; } else { dxgi.length_in_vsync_frames = 2; + }*/ + + dxgi.length_in_vsync_frames = configWindow.vsync; + f64 curTime = clock_elapsed_f64(); + if (curTime > sFrameTargetTime) { + sFrameTargetTime += sFrameTime; + if (curTime > sFrameTargetTime + sFrameTime * 3) { + sFrameTargetTime = curTime; + } + dxgi.dropped_frame = true; + return false; } + dxgi.dropped_frame = false; return true; } +static inline void sync_framerate_with_timer(void) { + f64 curTime = clock_elapsed_f64(); + if (curTime < sFrameTargetTime) { + u32 delayMs = (sFrameTargetTime - curTime) * 1000.0; + if (delayMs > 0) { + Sleep(delayMs); + } + } + sFrameTargetTime += sFrameTime; +} + static void gfx_dxgi_swap_buffers_begin(void) { - //dxgi.length_in_vsync_frames = 1; + sync_framerate_with_timer(); ThrowIfFailed(dxgi.swap_chain->Present(dxgi.length_in_vsync_frames, 0)); UINT this_present_id; if (dxgi.swap_chain->GetLastPresentCount(&this_present_id) == S_OK) { @@ -527,12 +562,12 @@ static void gfx_dxgi_swap_buffers_end(void) { QueryPerformanceCounter(&t0); QueryPerformanceCounter(&t1); - if (!dxgi.dropped_frame) { + /*if (!dxgi.dropped_frame) { if (dxgi.waitable_object != nullptr) { WaitForSingleObject(dxgi.waitable_object, INFINITE); } // else TODO: maybe sleep until some estimated time the frame will be shown to reduce lag - } + }*/ DXGI_FRAME_STATISTICS stats; dxgi.swap_chain->GetFrameStatistics(&stats); diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index 7208bb2d1..dd5759826 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -35,12 +35,17 @@ #include "src/pc/controller/controller_keyboard.h" +#include "pc/utils/misc.h" + // TODO: figure out if this shit even works #ifdef VERSION_EU # define FRAMERATE 25 #else # define FRAMERATE 30 #endif +// time between consequtive game frames +static const f64 sFrameTime = 1.0 / (2.0 * FRAMERATE); +static f64 sFrameTargetTime = 0; static SDL_Window *wnd; static SDL_GLContext ctx = NULL; @@ -51,11 +56,6 @@ static kb_callback_t kb_key_up = NULL; static void (*kb_all_keys_up)(void) = NULL; static void (*kb_text_input)(char*) = NULL; -// whether to use timer for frame control -static bool use_timer = true; -// time between consequtive game frames -static const int frame_time = 1000 / (2 * FRAMERATE); - const SDL_Scancode windows_scancode_table[] = { /* 0 1 2 3 4 5 6 7 */ /* 8 9 A B C D E F */ @@ -140,26 +140,7 @@ int test_vsync(void) { } static inline void gfx_sdl_set_vsync(const bool enabled) { - if (enabled) { - // try to detect refresh rate - SDL_GL_SetSwapInterval(1); - int vblanks = test_vsync(); - if (vblanks & 1) - vblanks = 0; // not divisible by 60, fuck that - else - vblanks /= 2; - if (vblanks) { - printf("determined swap interval: %d\n", vblanks); - SDL_GL_SetSwapInterval(vblanks); - use_timer = false; - return; - } else { - printf("could not determine swap interval, falling back to timer sync\n"); - } - } - - use_timer = true; - SDL_GL_SetSwapInterval(0); + SDL_GL_SetSwapInterval(enabled); } static void gfx_sdl_set_fullscreen(void) { @@ -334,21 +315,30 @@ static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callbac } static bool gfx_sdl_start_frame(void) { + f64 curTime = clock_elapsed_f64(); + if (curTime > sFrameTargetTime) { + sFrameTargetTime += sFrameTime; + if (curTime > sFrameTargetTime + sFrameTime * 3) { + sFrameTargetTime = curTime; + } + return false; + } return true; } static inline void sync_framerate_with_timer(void) { - static Uint32 last_time = 0; - // get base timestamp on the first frame (might be different from 0) - if (last_time == 0) last_time = SDL_GetTicks(); - const int elapsed = SDL_GetTicks() - last_time; - if (elapsed < frame_time) - SDL_Delay(frame_time - elapsed); - last_time += frame_time; + f64 curTime = clock_elapsed_f64(); + if (curTime < sFrameTargetTime) { + u32 delayMs = (sFrameTargetTime - curTime) * 1000.0; + if (delayMs > 0) { + SDL_Delay(delayMs); + } + } + sFrameTargetTime += sFrameTime; } static void gfx_sdl_swap_buffers_begin(void) { - if (use_timer) sync_framerate_with_timer(); + sync_framerate_with_timer(); SDL_GL_SwapWindow(wnd); } diff --git a/src/pc/utils/misc.c b/src/pc/utils/misc.c index d89d0f1b2..1163848cb 100644 --- a/src/pc/utils/misc.c +++ b/src/pc/utils/misc.c @@ -71,6 +71,10 @@ f32 clock_elapsed(void) { return (clock_elapsed_ns() / 1000000000.0f); } +f64 clock_elapsed_f64(void) { + return (clock_elapsed_ns() / 1000000000.0); +} + u32 clock_elapsed_ticks(void) { return (clock_elapsed_ns() * 3 / 100000000); } \ No newline at end of file diff --git a/src/pc/utils/misc.h b/src/pc/utils/misc.h index 765e03052..ff86e699b 100644 --- a/src/pc/utils/misc.h +++ b/src/pc/utils/misc.h @@ -4,6 +4,7 @@ float smoothstep(float edge0, float edge1, float x); void update_all_mario_stars(void); f32 clock_elapsed(void); +f64 clock_elapsed_f64(void); u32 clock_elapsed_ticks(void); #endif \ No newline at end of file