From 879e5af4eb1a8972fc944853a515e1003f94bd7c Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 11 Apr 2026 12:27:23 -0700 Subject: Fix simloop divergence --- simloop/include/simloop.h | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'simloop/include') diff --git a/simloop/include/simloop.h b/simloop/include/simloop.h index f267d40..6ee3b98 100644 --- a/simloop/include/simloop.h +++ b/simloop/include/simloop.h @@ -3,6 +3,37 @@ * This implements a simulation loop but in a way that the client retains * control flow. The client steps the loop and then checks whether the * simulation must be updated and/or the result rendered. + * + * If the simulation's update cannot keep up with the desired frame rate, then + * the loop degrades to match the simulation's rate by requesting a single + * update. + * + * Under a variable time delta, the loop could simply update the simulation + * with a large delta that puts the simulation back into the current clock + * time. Under a fixed time delta this isn't possible, and we seem to have two + * choices instead: + * + * a) Queue as many updates as necessary to bring the simulation back to the + * current clock time (time_diff / fixed_delta). + * + * b) Queue a single update. + * + * The issue with (a) is that the number of requested updates diverges and + * eventually the simulation appears to freeze. At every loop, the number of + * queued updates increases with respect to the last iteration as the + * simulation fails to keep up with the desired frame rate. Example: + * + * desired delta = 10ms (100 fps) + * actual delta = 20ms ( 50 fps) + * --------------------------- + * iter, sim time, clock time + * --------------------------- + * 0, 0, 0, initial state + * 1, 0, 10, queue 1 update + * 2, 10, 30, queue (30-10)/10 = 2 updates + * 3, 30, 70, queue (70-30)/10 = 4 updates + * 4, 70, 150, queue (150-70)/10 = 8 updates + * ... */ #pragma once @@ -21,8 +52,8 @@ typedef struct SimloopOut { time_delta render_elapsed; ///< Amount of time elapsed in the rendering. time_delta update_elapsed; ///< Amount of time elapsed in the simulation. time_delta update_dt; ///< Delta time for simulation updates. - int updates_pending; ///< Number of frames the simulation should produce. - bool should_render; ///< Whether the simulation should be rendered. + bool should_update; ///< Whether the simulation should update. + bool should_render; ///< Whether the simulation should be rendered. } SimloopOut; typedef struct SimloopTimeline { @@ -35,10 +66,13 @@ typedef struct Simloop { SimloopTimeline render; ///< Render timeline. uint64_t frame; ///< Frame counter. Timer* timer; + bool first_iter; } Simloop; /// Create a simulation loop. Simloop simloop_make(const SimloopArgs*); /// Step the simulation loop. +/// +/// The simulation always triggers a render of the initial state of simulation. void simloop_update(Simloop*, SimloopOut*); -- cgit v1.2.3