diff --git a/common/util/FrameLimiter.cpp b/common/util/FrameLimiter.cpp index a4f6ed57321..f3913bb30b6 100644 --- a/common/util/FrameLimiter.cpp +++ b/common/util/FrameLimiter.cpp @@ -1,5 +1,7 @@ #include "FrameLimiter.h" +#include "common/common_types.h" #include +#include double FrameLimiter::round_to_nearest_60fps(double current) { double one_frame = 1.f / 60.f; @@ -16,34 +18,18 @@ FrameLimiter::FrameLimiter() {} FrameLimiter::~FrameLimiter() {} -void FrameLimiter::run(double target_fps, - bool experimental_accurate_lag, - bool do_sleeps, - double engine_time) { - double target_seconds; - if (experimental_accurate_lag) { - target_seconds = round_to_nearest_60fps(engine_time); - } else { - target_seconds = 1.f / target_fps; - } - double remaining_time = target_seconds - m_timer.getSeconds(); - - if (do_sleeps && remaining_time > 0.001) { - std::this_thread::sleep_for(std::chrono::microseconds(int((remaining_time - 0.001) * 1e6))); - } - - while (remaining_time > 0) { - remaining_time = target_seconds - m_timer.getSeconds(); - } - - m_timer.start(); +void sleep_us(u64 us) { + std::this_thread::sleep_for(std::chrono::microseconds(us)); } #else - #define NOMINMAX #include +void sleep_us(u64 us) { + Sleep(us); +} + FrameLimiter::FrameLimiter() { timeBeginPeriod(1); } @@ -52,27 +38,34 @@ FrameLimiter::~FrameLimiter() { timeEndPeriod(0); } -void FrameLimiter::run(double target_fps, - bool experimental_accurate_lag, - bool do_sleeps, - double engine_time) { - double target_seconds; - if (experimental_accurate_lag) { - target_seconds = round_to_nearest_60fps(engine_time); - } else { - target_seconds = 1.f / target_fps; - } - double remaining_time = target_seconds - m_timer.getSeconds(); +#endif +void FrameLimiter::run(double target_fps, bool do_sleeps, double engine_time) { + // we want to exit this function when the current time reaches m_us_target. + // and update m_us_target for the next frame. + constexpr s64 max_lag_us = 20000; + constexpr s64 max_ahead_us = 50000; + + s64 entry_time = m_timer.getUs(); - if (do_sleeps && remaining_time > 0.001) { - Sleep((remaining_time * 1000) - 1); + // first, see if we're lagging too far behind: + if (entry_time > m_us_target + max_lag_us) { + m_us_target = entry_time - max_lag_us; } - while (remaining_time > 0) { - remaining_time = target_seconds - m_timer.getSeconds(); + // see if we're too far ahead + if (m_us_target > entry_time + max_ahead_us) { + m_us_target = entry_time + max_ahead_us; } - m_timer.start(); -} + // sleep! + s64 remaining = m_us_target - entry_time; + if (do_sleeps && remaining > 1000) { + sleep_us(remaining - 1000); + } + while (m_timer.getUs() < m_us_target) { + // wait.... + } -#endif + // now update the next target... + m_us_target += 1e6 * round_to_nearest_60fps(engine_time) * 60 / target_fps; +} \ No newline at end of file diff --git a/common/util/FrameLimiter.h b/common/util/FrameLimiter.h index 99361156226..69b55fdc09d 100644 --- a/common/util/FrameLimiter.h +++ b/common/util/FrameLimiter.h @@ -1,5 +1,6 @@ #pragma once +#include "common/common_types.h" #include "common/util/Timer.h" class FrameLimiter { @@ -7,10 +8,12 @@ class FrameLimiter { FrameLimiter(); ~FrameLimiter(); - void run(double target_fps, bool experimental_accurate_lag, bool do_sleeps, double engine_time); + void run(double target_fps, bool do_sleeps, double engine_time); private: double round_to_nearest_60fps(double current); Timer m_timer; + + s64 m_us_target = 0; }; \ No newline at end of file diff --git a/game/graphics/opengl_renderer/debug_gui.cpp b/game/graphics/opengl_renderer/debug_gui.cpp index bf9129e52e0..8d0349bcd43 100644 --- a/game/graphics/opengl_renderer/debug_gui.cpp +++ b/game/graphics/opengl_renderer/debug_gui.cpp @@ -112,7 +112,6 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) { target_fps = m_target_fps_text; } ImGui::Separator(); - ImGui::Checkbox("Accurate Lag Mode", &experimental_accurate_lag); ImGui::Checkbox("Sleep in Frame Limiter", &sleep_in_frame_limiter); ImGui::EndMenu(); } diff --git a/game/graphics/opengl_renderer/debug_gui.h b/game/graphics/opengl_renderer/debug_gui.h index 0ff186d6b64..3edf1c6654e 100644 --- a/game/graphics/opengl_renderer/debug_gui.h +++ b/game/graphics/opengl_renderer/debug_gui.h @@ -59,9 +59,8 @@ class OpenGlDebugGui { bool get_vsync_flag() { return m_vsync; } - bool framelimiter = false; + bool framelimiter = true; float target_fps = 60.f; - bool experimental_accurate_lag = false; bool sleep_in_frame_limiter = true; bool small_profiler = false; bool record_events = false; @@ -75,6 +74,6 @@ class OpenGlDebugGui { bool m_draw_debug = false; bool m_want_screenshot = false; char m_screenshot_save_name[256] = "screenshot.png"; - bool m_vsync = true; + bool m_vsync = false; float m_target_fps_text = 60.0; }; diff --git a/game/graphics/pipelines/opengl.cpp b/game/graphics/pipelines/opengl.cpp index 705b712de65..5d4b4ecd69d 100644 --- a/game/graphics/pipelines/opengl.cpp +++ b/game/graphics/pipelines/opengl.cpp @@ -120,7 +120,7 @@ static int gl_init(GfxSettings& settings) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // 4.3 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // core profile, not compat - // debug check + glfwWindowHint(GLFW_REFRESH_RATE, 60); if (settings.debug) { glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); } else { @@ -262,7 +262,6 @@ void render_game_frame(int width, int height, int lbox_width, int lbox_height) { // should be fine to remove this mutex if the game actually waits for vsync to call // send_chain again. but let's be safe for now. std::unique_lock lock(g_gfx_data->dma_mutex); - g_gfx_data->engine_timer.start(); g_gfx_data->has_data_to_render = false; g_gfx_data->sync_cv.notify_all(); } @@ -436,18 +435,23 @@ static void gl_render_display(GfxDisplay* display) { glfwSwapInterval(req_vsync); } + g_gfx_data->last_engine_time = g_gfx_data->engine_timer.getSeconds(); + // actual vsync g_gfx_data->debug_gui.finish_frame(); { auto p = scoped_prof("swap-buffers"); glfwSwapBuffers(window); } + if (g_gfx_data->debug_gui.framelimiter) { auto p = scoped_prof("frame-limiter"); - g_gfx_data->frame_limiter.run( - g_gfx_data->debug_gui.target_fps, g_gfx_data->debug_gui.experimental_accurate_lag, - g_gfx_data->debug_gui.sleep_in_frame_limiter, g_gfx_data->last_engine_time); + g_gfx_data->frame_limiter.run(g_gfx_data->debug_gui.target_fps, + g_gfx_data->debug_gui.sleep_in_frame_limiter, + g_gfx_data->last_engine_time); } + g_gfx_data->engine_timer.start(); + g_gfx_data->debug_gui.start_frame(); prof().instant_event("ROOT"); update_global_profiler(); @@ -498,7 +502,6 @@ u32 gl_sync_path() { return 0; } std::unique_lock lock(g_gfx_data->sync_mutex); - g_gfx_data->last_engine_time = g_gfx_data->engine_timer.getSeconds(); if (!g_gfx_data->has_data_to_render) { return 0; } diff --git a/goal_src/engine/draw/drawable.gc b/goal_src/engine/draw/drawable.gc index ea291d61c99..180192bf5ec 100644 --- a/goal_src/engine/draw/drawable.gc +++ b/goal_src/engine/draw/drawable.gc @@ -1276,8 +1276,8 @@ (define *screen-shot* #f) (defun display-frame-start ((disp display) (new-frame-idx int) (odd-even int)) "Set up a new frame. Call this before drawing anything. - new-frame-idx is the display frame that will be set up. - odd-even is the odd-even of the new frame" + new-frame-idx is the display frame that will be set up. + odd-even is the odd-even of the new frame" ;; due to a HW bug in the PS2, you must set this. @@ -1292,7 +1292,7 @@ ) ) - ) + ) ) (let ((float-time-ratio (/ (the float (timer-count (the-as timer-bank #x10000800))) (the float *ticks-per-frame*)))) @@ -1304,18 +1304,29 @@ ;; if we actually do miss a frame, the time ratio will be around 2. (#when PC_PORT + (if (< float-time-ratio 3.3) + (set! time-ratio 3.0) + ) + (if (< float-time-ratio 2.3) + (set! time-ratio 2.0) + ) (if (< float-time-ratio 1.3) (set! time-ratio 1.0) ) + + #| (if (> time-ratio 1.) (format #t "LAG ~f frames~%" (- time-ratio 1.)) ) |# ) + ;; (format *stdcon* "time: ~f and ~f~%" time-ratio float-time-ratio) ) + + ;; inform display system of our speed. This will adjust the scaling used in all physics calculations (set-time-ratios *display* time-ratio)