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/test/simloop_test.c | 69 +++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 49 deletions(-) (limited to 'simloop/test') diff --git a/simloop/test/simloop_test.c b/simloop/test/simloop_test.c index 790ad73..61e7dff 100644 --- a/simloop/test/simloop_test.c +++ b/simloop/test/simloop_test.c @@ -29,65 +29,38 @@ static uint64_t xorshift64(XorShift64State* state) { // ----------------------------------------------------------------------------- // Tests. -/// At time/frame 0: -/// 1. An initial render is always triggered. -/// 2. No update is triggered (not enough time passed). +/// At time/frame 0, no update is triggered (not enough time passed). TEST_CASE(simloop_initial_render) { Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = 10}); SimloopOut simout; simloop_update(&simloop, 0, &simout); - TEST_TRUE(simout.should_render); TEST_TRUE(!simout.should_update); TEST_EQUAL(simout.frame, 0); } -/// A frame is not re-rendered if time does not advance. -/// This applies whether rendering is frame-rate capped or unlimited, and -/// whether we are in the initial frame or a subsequent one. -void simloop_render_not_retriggered( - struct test_case_metadata* metadata, int max_render_fps, - bool initial_frame) { - Simloop simloop = simloop_make( - &(SimloopArgs){.update_fps = 10, .max_render_fps = max_render_fps}); +/// The simulation is not updated if time does not advance. +/// This applies generally to any time > 0. +TEST_CASE(simloop_render_not_retriggered) { + Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = 10}); SimloopOut simout; + // Advance time by some amount to get past t=0. + simloop_update(&simloop, 1, &simout); + + // Now "advance" by 0. + const uint64_t frame_before = simout.frame; simloop_update(&simloop, 0, &simout); + const uint64_t frame_after = simout.frame; - TEST_TRUE(simout.should_render); TEST_TRUE(!simout.should_update); - TEST_EQUAL(simout.frame, 0); - - if (!initial_frame) { - // Advance time beyond the initial frame. - simloop_update(&simloop, 1, &simout); - } - - for (int i = 0; i < 10; i++) { - // Note that time does not advance here. - simloop_update(&simloop, 0, &simout); - TEST_TRUE(!simout.should_render); - TEST_TRUE(!simout.should_update); - TEST_EQUAL(simout.frame, 0); - } -} -TEST_CASE(simloop_render_not_retriggered_capped_initial_frame) { - simloop_render_not_retriggered(metadata, 10, true); -} -TEST_CASE(simloop_render_not_retriggered_unlimited_initial_frame) { - simloop_render_not_retriggered(metadata, 0, true); -} -TEST_CASE(simloop_render_not_retriggered_capped_subsequent_frame) { - simloop_render_not_retriggered(metadata, 10, false); -} -TEST_CASE(simloop_render_not_retriggered_unlimited_subsequent_frame) { - simloop_render_not_retriggered(metadata, 0, false); + TEST_EQUAL(frame_before, frame_after); } /// A simulation loop with no render frame cap: /// 1. Updates based on the desired update frame rate. -/// 2. Renders at every step. +/// 2. Does not throttle rendering. TEST_CASE(simloop_no_render_frame_cap) { constexpr int UPDATE_FPS = 10; // 100ms delta const simloop_time_t UPDATE_DDT = @@ -104,19 +77,19 @@ TEST_CASE(simloop_no_render_frame_cap) { simloop_update(&simloop, 0, &simout); TEST_TRUE(!simout.should_update); // Time has not advanced. - TEST_TRUE(simout.should_render); // Initial render. + TEST_EQUAL(simout.throttle, 0); // No throttling with no render frame cap. for (simloop_time_t t = STEP; t <= SIM_DURATION_SEC; t += STEP) { simloop_update(&simloop, STEP, &simout); const bool expect_update = (t % UPDATE_DDT) == 0; TEST_EQUAL(simout.should_update, expect_update); - TEST_TRUE(simout.should_render); // Always renders. + TEST_EQUAL(simout.throttle, 0); } } /// A simulation loop with a render frame cap: /// 1. Updates based on the desired update frame rate. -/// 2. Renders based on the desired render frame rate. +/// 2. Throttles rendering based on the desired render frame rate. TEST_CASE(simloop_with_render_frame_cap) { constexpr int UPDATE_FPS = 10; // 100ms delta constexpr int RENDER_FPS = 5; // 200ms delta @@ -130,7 +103,6 @@ TEST_CASE(simloop_with_render_frame_cap) { // We need simulation time to be an exact multiple of the desired deltas for // the modulo comparisons below. TEST_TRUE((UPDATE_DDT % STEP) == 0); - TEST_TRUE((RENDER_DDT % STEP) == 0); Simloop simloop = simloop_make( &(SimloopArgs){.update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS}); @@ -138,13 +110,12 @@ TEST_CASE(simloop_with_render_frame_cap) { simloop_update(&simloop, 0, &simout); TEST_TRUE(!simout.should_update); // Time has not advanced. - TEST_TRUE(simout.should_render); // Initial render. + TEST_EQUAL(simout.throttle, 0); // No throttle since time has not advanced. for (simloop_time_t t = STEP; t <= SIM_DURATION_SEC; t += STEP) { simloop_update(&simloop, STEP, &simout); - // A render is still expected at time 0. - TEST_EQUAL(simout.should_render, (t % RENDER_DDT) == 0); TEST_EQUAL(simout.should_update, (t % UPDATE_DDT) == 0); + TEST_NOTEQUAL(simout.throttle, 0); } } @@ -166,12 +137,12 @@ TEST_CASE(simloop_percent_frame_01_large_jump) { simloop_update(&simloop, 0, &simout); TEST_TRUE(!simout.should_update); // Time has not advanced. - TEST_TRUE(simout.should_render); // Initial render. for (simloop_time_t t = STEP; t <= SIM_DURATION_SEC; t += STEP) { simloop_update(&simloop, STEP, &simout); TEST_TRUE(simout.should_update); // Tries to catch up to clock. - TEST_TRUE(simout.should_render); + TEST_TRUE(0. <= simout.percent_frame); + TEST_TRUE(simout.percent_frame <= 1.); } } -- cgit v1.2.3