From e5fe20ad83e01b3c8262ac90af984da47d8a16a1 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sun, 12 Apr 2026 10:48:04 -0700 Subject: Work in terms of time deltas; remove dependency on timer module --- simloop/test/simloop_test.c | 102 +++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 48 deletions(-) (limited to 'simloop/test') diff --git a/simloop/test/simloop_test.c b/simloop/test/simloop_test.c index e8efd94..603a38c 100644 --- a/simloop/test/simloop_test.c +++ b/simloop/test/simloop_test.c @@ -1,10 +1,17 @@ #include #include -#include #include +// ----------------------------------------------------------------------------- +// Time. + +static simloop_time_t time_delta_from_sec(double seconds) { + static constexpr double NANOS_PER_SEC = 1e9; + return (simloop_time_t)(seconds * NANOS_PER_SEC); +} + // ----------------------------------------------------------------------------- // Randomness. @@ -12,7 +19,7 @@ typedef struct { uint64_t a; } XorShift64State; -uint64_t xorshift64(XorShift64State* state) { +static uint64_t xorshift64(XorShift64State* state) { uint64_t x = state->a; x ^= x << 7; x ^= x >> 9; @@ -26,12 +33,10 @@ uint64_t xorshift64(XorShift64State* state) { /// 1. An initial render is always triggered. /// 2. No update is triggered (not enough time passed). TEST_CASE(simloop_initial_render) { - Timer timer = {}; - Simloop simloop = - simloop_make(&(SimloopArgs){.update_fps = 10, .timer = &timer}); + Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = 10}); SimloopOut simout; - simloop_update(&simloop, &simout); + simloop_update(&simloop, 0, &simout); TEST_TRUE(simout.should_render); TEST_TRUE(!simout.should_update); @@ -41,12 +46,11 @@ TEST_CASE(simloop_initial_render) { /// The initial render is not re-triggered if there is a render frame rate cap /// and time does not advance. TEST_CASE(simloop_initial_render_not_retriggered) { - Timer timer = {}; - Simloop simloop = simloop_make( - &(SimloopArgs){.update_fps = 10, .max_render_fps = 10, .timer = &timer}); + Simloop simloop = + simloop_make(&(SimloopArgs){.update_fps = 10, .max_render_fps = 10}); SimloopOut simout; - simloop_update(&simloop, &simout); + simloop_update(&simloop, 0, &simout); TEST_TRUE(simout.should_render); TEST_TRUE(!simout.should_update); @@ -54,7 +58,7 @@ TEST_CASE(simloop_initial_render_not_retriggered) { for (int i = 0; i < 10; i++) { // Note that time does not advance. - simloop_update(&simloop, &simout); + simloop_update(&simloop, 0, &simout); TEST_TRUE(!simout.should_render); TEST_TRUE(!simout.should_update); TEST_EQUAL(simout.frame, 0); @@ -65,27 +69,27 @@ TEST_CASE(simloop_initial_render_not_retriggered) { /// 1. Updates based on the desired update frame rate. /// 2. Renders at every loop (provided there are updates). TEST_CASE(simloop_no_render_frame_cap) { - constexpr int UPDATE_FPS = 10; // 100ms delta - const time_delta UPDATE_DDT = sec_to_time_delta(1.0 / (double)UPDATE_FPS); - const time_delta STEP = sec_to_time_delta(1); - const time_delta SIM_TIME_SEC = sec_to_time_delta(30); + constexpr int UPDATE_FPS = 10; // 100ms delta + const simloop_time_t UPDATE_DDT = + time_delta_from_sec(1.0 / (double)UPDATE_FPS); + const simloop_time_t STEP = time_delta_from_sec(1); + const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(30); // We need simulation time to be an exact multiple of the desired deltas for // the modulo comparisons below. TEST_TRUE((STEP % UPDATE_DDT) == 0); - Timer timer = {}; - Simloop simloop = - simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS, .timer = &timer}); + simloop_time_t dt = 0; + Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS}); SimloopOut simout; - for (time_delta t = 0; t < SIM_TIME_SEC; t += STEP) { - timer_advance(&timer, t); - simloop_update(&simloop, &simout); + for (simloop_time_t t = 0; t <= SIM_TIME_SEC; t += STEP) { + simloop_update(&simloop, dt, &simout); const bool expect_update = (t > 0) && ((t % UPDATE_DDT) == 0); // A render is still expected at time 0. TEST_EQUAL(simout.should_render, (t == 0) || expect_update); TEST_EQUAL(simout.should_update, expect_update); + dt = STEP; } } @@ -93,29 +97,31 @@ TEST_CASE(simloop_no_render_frame_cap) { /// 1. Updates based on the desired update frame rate. /// 2. Renders 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 - const time_delta UPDATE_DDT = sec_to_time_delta(1.0 / (double)UPDATE_FPS); - const time_delta RENDER_DDT = sec_to_time_delta(1.0 / (double)RENDER_FPS); - const time_delta STEP = sec_to_time_delta(0.1); // 100ms - const time_delta SIM_TIME_SEC = sec_to_time_delta(30); + constexpr int UPDATE_FPS = 10; // 100ms delta + constexpr int RENDER_FPS = 5; // 200ms delta + const simloop_time_t UPDATE_DDT = + time_delta_from_sec(1.0 / (double)UPDATE_FPS); + const simloop_time_t RENDER_DDT = + time_delta_from_sec(1.0 / (double)RENDER_FPS); + const simloop_time_t STEP = time_delta_from_sec(0.1); // 100ms + const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(30); // 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); - Timer timer = {}; - Simloop simloop = simloop_make(&(SimloopArgs){ - .update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS, .timer = &timer}); + simloop_time_t dt = 0; + Simloop simloop = simloop_make( + &(SimloopArgs){.update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS}); SimloopOut simout; - for (time_delta t = 0; t < SIM_TIME_SEC; t += STEP) { - timer_advance(&timer, t); - simloop_update(&simloop, &simout); + for (simloop_time_t t = 0; t <= SIM_TIME_SEC; t += STEP) { + simloop_update(&simloop, dt, &simout); // A render is still expected at time 0. TEST_EQUAL(simout.should_render, (t % RENDER_DDT) == 0); TEST_EQUAL(simout.should_update, (t > 0) && ((t % UPDATE_DDT) == 0)); + dt = STEP; } } @@ -130,16 +136,16 @@ TEST_CASE(simloop_with_render_frame_cap) { /// the client to decide whether to update just once or as many times as /// requested, depending on whether they want determinism or convergence. TEST_CASE(simloop_determinism) { - constexpr int UPDATE_FPS = 100; // 10ms delta - const time_delta RANDOM_STEPS[] = { - sec_to_time_delta(0.007), // 7ms - sec_to_time_delta(0.005), // 5ms - sec_to_time_delta(0.003), // 3ms + constexpr int UPDATE_FPS = 100; // 10ms delta + const simloop_time_t RANDOM_STEPS[] = { + time_delta_from_sec(0.007), // 7ms + time_delta_from_sec(0.005), // 5ms + time_delta_from_sec(0.003), // 3ms }; constexpr uint64_t NUM_RANDOM_STEPS = sizeof(RANDOM_STEPS) / sizeof(RANDOM_STEPS[0]); - const time_delta SIM_TIME_SEC = sec_to_time_delta(10); - constexpr float ADD = 0.123f; + const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(10); + constexpr float ADD = 0.123f; typedef struct Simulation { int iter_count; @@ -153,26 +159,26 @@ TEST_CASE(simloop_determinism) { } Simulation sim[2] = {0}; - XorShift64State xss = (XorShift64State){12069019817132197873}; + XorShift64State xss = (XorShift64State){12069019817132197873ULL}; // Perform two simulations with random clock-time steps. for (int s = 0; s < 2; ++s) { - Timer timer = {}; - Simloop simloop = - simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS, .timer = &timer}); + simloop_time_t dt = 0; + Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS}); SimloopOut simout; - for (time_delta t = 0; t < SIM_TIME_SEC;) { - timer_advance(&timer, t); - simloop_update(&simloop, &simout); + for (simloop_time_t t = 0; t <= SIM_TIME_SEC;) { + simloop_update(&simloop, dt, &simout); if (simout.should_update) { UPDATE_SIMULATION(sim[s]); } // Advance time with a random step. - const time_delta step = RANDOM_STEPS[xorshift64(&xss) % NUM_RANDOM_STEPS]; + const simloop_time_t step = + RANDOM_STEPS[xorshift64(&xss) % NUM_RANDOM_STEPS]; t += step; + dt = step; } } -- cgit v1.2.3