#include #include static simloop_time_t ddt_from_fps(int fps) { static constexpr double NANOSECONDS = 1e9; return (fps == 0) ? 0 : (simloop_time_t)(NANOSECONDS / (double)fps); } Simloop simloop_make(const SimloopArgs* args) { assert(args); assert(args->update_fps > 0); return (Simloop){ .frame = 0, .update = (SimloopTimeline){ .ddt = ddt_from_fps(args->update_fps), .time = 0, }, .render = (SimloopTimeline){ .ddt = ddt_from_fps(args->max_render_fps), .time = 0, }, .first_iter = true, .updates_since_last_render = false, }; } static bool step_update(const Simloop* sim, SimloopTimeline* timeline) { assert(sim); assert(timeline); assert(timeline->ddt > 0); const simloop_time_t dt = sim->clock - timeline->time; const bool should_step = dt >= timeline->ddt; timeline->time += should_step ? timeline->ddt : 0; return should_step; } static bool step_render(const Simloop* sim, SimloopTimeline* timeline) { assert(sim); assert(timeline); bool render = false; if (timeline->ddt > 0) { render = step_update(sim, timeline); } else { timeline->time = sim->clock; render = true; } return render; } void simloop_update(Simloop* sim, simloop_time_t dt, SimloopOut* out) { assert(sim); assert(out); sim->clock += dt; // Simulation update. const bool updated = step_update(sim, &sim->update); sim->updates_since_last_render = sim->updates_since_last_render || updated; // Simulation render. const bool rendered = (sim->updates_since_last_render && step_render(sim, &sim->render)) || (sim->first_iter); // Trigger an initial render on the first frame. sim->updates_since_last_render = sim->updates_since_last_render && !out->should_render; // Loop state update. sim->frame += (updated ? 1 : 0); sim->first_iter = false; out->frame = sim->frame; out->render_elapsed = sim->render.time; out->update_elapsed = sim->update.time; out->update_dt = sim->update.ddt; out->should_update = updated; out->should_render = rendered; }