From 6482f3995baf9515158d999db925ef35158cfba5 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 2 May 2026 14:35:43 -0700 Subject: Throttle rendering to reduce CPU usage --- simloop/src/simloop.c | 84 +++++++++++++++------------------------------------ 1 file changed, 24 insertions(+), 60 deletions(-) (limited to 'simloop/src') diff --git a/simloop/src/simloop.c b/simloop/src/simloop.c index b8547fd..d231ab3 100644 --- a/simloop/src/simloop.c +++ b/simloop/src/simloop.c @@ -20,87 +20,51 @@ Simloop simloop_make(const SimloopArgs* args) { .ddt = ddt_from_fps(args->update_fps), .time = 0, }, - .render = - (SimloopTimeline){ - .ddt = ddt_from_fps(args->max_render_fps), - .time = 0, - }, - .percent_frame = 0., - .first_iter = true, + .render_ddt = ddt_from_fps(args->max_render_fps), }; } -static bool step_update(const Simloop* sim, SimloopTimeline* timeline) { +void simloop_update(Simloop* sim, simloop_time_t dt, SimloopOut* out) { assert(sim); - assert(timeline); - assert(timeline->ddt > 0); + assert(out); + + sim->clock += dt; + // Simulation update. // If the update falls behind the clock, we advance by a single ddt increment // per loop iteration here and give it a chance to catch up over subsequent // iterations. // This has the implication that percent_frame can fall out of range (>1) if // we are not careful with how it is defined. See the general update function // below. - 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 { - render = timeline->time < sim->clock; - timeline->time = sim->clock; - } - return render; -} - -void simloop_update(Simloop* sim, simloop_time_t dt, SimloopOut* out) { - assert(sim); - assert(out); - - sim->clock += dt; - - // Simulation update. - const bool update_this_tick = step_update(sim, &sim->update); + const simloop_time_t delta = sim->clock - sim->update.time; + const bool update_this_tick = delta >= sim->update.ddt; + sim->update.time += update_this_tick ? sim->update.ddt : 0; - // Simulation render. - const bool render_this_tick = - step_render(sim, &sim->render) || - sim->first_iter; // Trigger an initial render on the first frame. + // Loop-state update. + sim->frame += (update_this_tick ? 1 : 0); // Interpolator for smooth animation. - // If rendering is not frame-rate capped, then its timeline should always be - // at least as recent as the update's. Otherwise, it is possible for the - // rendering timeline to be behind. // If the update falls behind the clock, then percent_frame can fall out of // range (>1) if we are not careful. We impose that it is strictly never >1 // to account for this case. assert(sim->update.ddt > 0); - assert( - (sim->render.ddt == 0) ? (sim->update.time <= sim->render.time) : true); - sim->percent_frame = - (sim->render.time >= sim->update.time) - ? min(1., ((double)(sim->render.time - sim->update.time) / - (double)sim->update.ddt)) - : sim->percent_frame; - assert((0. <= sim->percent_frame) && (sim->percent_frame <= 1.)); - - // Loop state update. - sim->frame += (update_this_tick ? 1 : 0); - sim->first_iter = false; + assert(sim->update.time <= sim->clock); + out->percent_frame = min( + 1., (double)(sim->clock - sim->update.time) / (double)sim->update.ddt); + assert((0. <= out->percent_frame) && (out->percent_frame <= 1.)); + + // Render frame rate throttle. + // Note that if no max render fps is given, then render_ddt is 0. The logic + // works for both render_ddt>0 and =0. + // Need to be careful with subtraction since the quantities are unsigned. + // Subtract an epsilon to account for delays in thread scheduling. + static const simloop_time_t eps = 50'000; // 50us + out->throttle = + (sim->render_ddt > (dt - eps)) ? (sim->render_ddt - eps - dt) : 0; out->frame = sim->frame; - out->render_elapsed = sim->render.time; out->update_elapsed = sim->update.time; out->update_dt = sim->update.ddt; - out->percent_frame = sim->percent_frame; out->should_update = update_this_tick; - out->should_render = render_this_tick; } -- cgit v1.2.3