/* Simulation loop module. * * 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 #include #include typedef struct SimloopArgs { int update_fps; ///< Update frame rate. Must be >0. int max_render_fps; ///< Render frame rate cap. 0 to disable. Timer* timer; ///< Timer that drives the simulation. } SimloopArgs; typedef struct SimloopOut { uint64_t frame; ///< Frame counter. 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. bool should_update; ///< Whether the simulation should update. bool should_render; ///< Whether the simulation should be rendered. } SimloopOut; typedef struct SimloopTimeline { time_delta ddt; ///< Desired delta time. time_point last_step; ///< Time of the last simulation step. } SimloopTimeline; typedef struct Simloop { SimloopTimeline update; ///< Update timeline. 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*);