diff --git a/runtime/Includes/Core/Fps.h b/runtime/Includes/Core/Fps.h index 6cebc77..05364f4 100644 --- a/runtime/Includes/Core/Fps.h +++ b/runtime/Includes/Core/Fps.h @@ -1,26 +1,30 @@ #ifndef __MLX_FPS__ #define __MLX_FPS__ +#include + namespace mlx { + typedef std::chrono::steady_clock fps_clock; + class FpsManager { public: FpsManager() = default; void Init(); - bool Update(); - inline void SetMaxFPS(std::uint32_t fps) noexcept { m_max_fps = fps; m_ns = 1000000000.0 / fps; } + void WaitUntilNextFrame(); + inline void SetMaxFPS(std::uint32_t fps) noexcept { m_target_delta = fps_clock::duration(std::chrono::seconds(1)) / fps;} ~FpsManager() = default; private: - double m_ns = 1000000000.0 / 1'337'000.0; - std::int64_t m_fps_before = 0; - std::int64_t m_fps_now = 0; - std::int64_t m_timer = 0; - std::uint32_t m_max_fps = 1'337'000; - std::uint32_t m_fps_elapsed_time = 0; + fps_clock::time_point m_current_time; + fps_clock::time_point m_target_time; + fps_clock::time_point m_last_time_record; + fps_clock::duration m_delta_time = fps_clock::duration().zero(); + fps_clock::duration m_target_delta = fps_clock::duration().zero(); + fps_clock::duration m_sleep_margin = std::chrono::microseconds(200); }; } diff --git a/runtime/Sources/Core/Application.cpp b/runtime/Sources/Core/Application.cpp index ef253b3..009c800 100644 --- a/runtime/Sources/Core/Application.cpp +++ b/runtime/Sources/Core/Application.cpp @@ -8,7 +8,7 @@ namespace mlx { - Application::Application() : p_mem_manager(std::make_unique()), p_sdl_manager(std::make_unique()), m_fps(), m_in() + Application::Application() : p_mem_manager(std::make_unique()), p_sdl_manager(std::make_unique()), m_fps(), m_in() { MLX_PROFILE_FUNCTION(); std::srand(std::time(nullptr)); @@ -33,8 +33,7 @@ namespace mlx while(m_in.IsRunning()) { - if(!m_fps.Update()) - continue; + m_fps.WaitUntilNextFrame(); m_in.FetchInputs(); diff --git a/runtime/Sources/Core/Bridge.cpp b/runtime/Sources/Core/Bridge.cpp index 76ee9f6..4123d6a 100644 --- a/runtime/Sources/Core/Bridge.cpp +++ b/runtime/Sources/Core/Bridge.cpp @@ -45,10 +45,9 @@ extern "C" void mlx_set_fps_goal(mlx_context mlx, int fps) { MLX_CHECK_APPLICATION_POINTER(mlx); - if(fps < 0) - mlx::Error("You cannot set a negative FPS cap (nice try)"); - else - mlx->app->SetFPSCap(static_cast(fps)); + if(fps <= 0) + fps = -1; + mlx->app->SetFPSCap(static_cast(fps)); } void mlx_destroy_context(mlx_context mlx) @@ -313,14 +312,14 @@ extern "C" mlx::Error("Font loader: filepath is NULL"); return; } - + std::filesystem::path file(filepath); if (std::strcmp(filepath, "default") != 0 && !std::filesystem::exists(file)) { mlx::Error("TTF loader: unable to find file '%'", filepath); return; } - + if(std::strcmp(filepath, "default") != 0) { if(file.extension() != ".ttf" && file.extension() != ".tte") @@ -350,7 +349,7 @@ extern "C" mlx::Error("Font loader: filepath is NULL"); return; } - + std::filesystem::path file(filepath); if (std::strcmp(filepath, "default") != 0 && !std::filesystem::exists(file)) { diff --git a/runtime/Sources/Core/Fps.cpp b/runtime/Sources/Core/Fps.cpp index a464f67..d97879c 100644 --- a/runtime/Sources/Core/Fps.cpp +++ b/runtime/Sources/Core/Fps.cpp @@ -1,30 +1,37 @@ #include #include +#ifndef __APPLE__ +#include +#endif + namespace mlx { void FpsManager::Init() { - m_timer = static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); - m_fps_before = m_timer; - m_fps_now = m_timer; + m_current_time = fps_clock::now(); + m_target_time = m_current_time + m_target_delta; } - bool FpsManager::Update() + void FpsManager::WaitUntilNextFrame() { - using namespace std::chrono_literals; - m_fps_now = static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); - - if(std::chrono::duration{m_fps_now - m_timer} >= 1s) - m_timer += m_fps_now; - - m_fps_elapsed_time = m_fps_now - m_fps_before; - if(m_fps_elapsed_time >= m_ns) + m_current_time = fps_clock::now(); + if(m_current_time < m_target_time) { - m_fps_before += m_ns; - return true; + std::this_thread::sleep_until(m_target_time - m_sleep_margin); + m_current_time = fps_clock::now(); + while (m_current_time < m_target_time) + { + #ifndef __APPLE__ + _mm_pause(); // reduces CPU usage on x86 without yielding + #endif + m_current_time = fps_clock::now(); + } } - std::this_thread::sleep_for(std::chrono::duration(m_ns - 1)); - return false; + else if (m_target_time < m_current_time - m_target_delta * 4) + m_target_time = m_current_time; + m_target_time += m_target_delta; + m_delta_time = m_current_time - m_last_time_record; + m_last_time_record = m_current_time; } }