mirror of
https://github.com/seekrs/MacroLibX.git
synced 2026-09-02 23:40:01 +02:00
Fix inconsistencies with the FPS scheduler (#241)
The current Fps manager will sometime skip the intended delay and start the next frame immediately, I'm not 100% sure why it happens, but I rewrote most of it to stay consistent and improved the overall precision by compensating for OS wake delay after sleep and finishing with a busy wait I also changed the behavior of mlx_set_fps_goal to allow zero/negative values and treat them as uncapped (like most graphics libraries)
This commit is contained in:
@@ -1,26 +1,30 @@
|
||||
#ifndef __MLX_FPS__
|
||||
#define __MLX_FPS__
|
||||
|
||||
#include <chrono>
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,7 @@ namespace mlx
|
||||
|
||||
while(m_in.IsRunning())
|
||||
{
|
||||
if(!m_fps.Update())
|
||||
continue;
|
||||
m_fps.WaitUntilNextFrame();
|
||||
|
||||
m_in.FetchInputs();
|
||||
|
||||
|
||||
@@ -45,9 +45,8 @@ 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
|
||||
if(fps <= 0)
|
||||
fps = -1;
|
||||
mlx->app->SetFPSCap(static_cast<std::uint32_t>(fps));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
#include <PreCompiled.h>
|
||||
#include <Core/Fps.h>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
namespace mlx
|
||||
{
|
||||
void FpsManager::Init()
|
||||
{
|
||||
m_timer = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(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::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
|
||||
|
||||
if(std::chrono::duration<std::uint64_t>{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_for(std::chrono::duration<double, std::nano>(m_ns - 1));
|
||||
return false;
|
||||
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();
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user