diff options
| author | 3gg <3gg@shellblade.net> | 2026-04-12 10:48:04 -0700 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2026-04-12 10:48:04 -0700 |
| commit | e5fe20ad83e01b3c8262ac90af984da47d8a16a1 (patch) | |
| tree | ffb55eabccc2018cb0359dc054fdadebfc815d65 /simloop | |
| parent | 9dceaee478545ed8df89722f5c90bb171de100e8 (diff) | |
Work in terms of time deltas; remove dependency on timer module
Diffstat (limited to 'simloop')
| -rw-r--r-- | simloop/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | simloop/include/simloop.h | 31 | ||||
| -rw-r--r-- | simloop/src/simloop.c | 56 | ||||
| -rw-r--r-- | simloop/test/simloop_test.c | 102 |
4 files changed, 99 insertions, 93 deletions
diff --git a/simloop/CMakeLists.txt b/simloop/CMakeLists.txt index 1997e2f..fe7f954 100644 --- a/simloop/CMakeLists.txt +++ b/simloop/CMakeLists.txt | |||
| @@ -13,9 +13,6 @@ add_library(simloop | |||
| 13 | target_include_directories(simloop PUBLIC | 13 | target_include_directories(simloop PUBLIC |
| 14 | include) | 14 | include) |
| 15 | 15 | ||
| 16 | target_link_libraries(simloop PUBLIC | ||
| 17 | timer) | ||
| 18 | |||
| 19 | target_compile_options(simloop PRIVATE -Wall -Wextra -Wpedantic) | 16 | target_compile_options(simloop PRIVATE -Wall -Wextra -Wpedantic) |
| 20 | 17 | ||
| 21 | # Test | 18 | # Test |
diff --git a/simloop/include/simloop.h b/simloop/include/simloop.h index 1e5b4e0..b08dcac 100644 --- a/simloop/include/simloop.h +++ b/simloop/include/simloop.h | |||
| @@ -37,35 +37,34 @@ | |||
| 37 | */ | 37 | */ |
| 38 | #pragma once | 38 | #pragma once |
| 39 | 39 | ||
| 40 | #include <timer.h> | ||
| 41 | |||
| 42 | #include <stdint.h> | 40 | #include <stdint.h> |
| 43 | 41 | ||
| 42 | typedef uint64_t simloop_time_t; | ||
| 43 | |||
| 44 | typedef struct SimloopArgs { | 44 | typedef struct SimloopArgs { |
| 45 | int update_fps; ///< Update frame rate. Must be >0. | 45 | int update_fps; ///< Update frame rate. Must be >0. |
| 46 | int max_render_fps; ///< Render frame rate cap. 0 to disable. | 46 | int max_render_fps; ///< Render frame rate cap. 0 to disable. |
| 47 | Timer* timer; ///< Timer that drives the simulation. | ||
| 48 | } SimloopArgs; | 47 | } SimloopArgs; |
| 49 | 48 | ||
| 50 | typedef struct SimloopOut { | 49 | typedef struct SimloopOut { |
| 51 | uint64_t frame; ///< Frame counter. | 50 | uint64_t frame; ///< Frame counter. |
| 52 | time_delta render_elapsed; ///< Amount of time elapsed in the rendering. | 51 | simloop_time_t render_elapsed; ///< Amount of time elapsed in the rendering. |
| 53 | time_delta update_elapsed; ///< Amount of time elapsed in the simulation. | 52 | simloop_time_t update_elapsed; ///< Amount of time elapsed in the simulation. |
| 54 | time_delta update_dt; ///< Delta time for simulation updates. | 53 | simloop_time_t update_dt; ///< Delta time for simulation updates. |
| 55 | bool should_update; ///< Whether the simulation should update. | 54 | bool should_update; ///< Whether the simulation should update. |
| 56 | bool should_render; ///< Whether the simulation should be rendered. | 55 | bool should_render; ///< Whether the simulation should be rendered. |
| 57 | } SimloopOut; | 56 | } SimloopOut; |
| 58 | 57 | ||
| 59 | typedef struct SimloopTimeline { | 58 | typedef struct SimloopTimeline { |
| 60 | time_delta ddt; ///< Desired delta time. | 59 | simloop_time_t ddt; ///< Desired delta time. |
| 61 | time_point last_step; ///< Time of the last simulation step. | 60 | simloop_time_t time; ///< Time point of the last simulation step. |
| 62 | } SimloopTimeline; | 61 | } SimloopTimeline; |
| 63 | 62 | ||
| 64 | typedef struct Simloop { | 63 | typedef struct Simloop { |
| 64 | simloop_time_t clock; ///< Tracks simulation time. | ||
| 65 | uint64_t frame; ///< Frame counter, number of updates done. | ||
| 65 | SimloopTimeline update; ///< Update timeline. | 66 | SimloopTimeline update; ///< Update timeline. |
| 66 | SimloopTimeline render; ///< Render timeline. | 67 | SimloopTimeline render; ///< Render timeline. |
| 67 | uint64_t frame; ///< Frame counter. | ||
| 68 | Timer* timer; | ||
| 69 | bool first_iter; | 68 | bool first_iter; |
| 70 | bool updates_since_last_render; | 69 | bool updates_since_last_render; |
| 71 | } Simloop; | 70 | } Simloop; |
| @@ -76,4 +75,4 @@ Simloop simloop_make(const SimloopArgs*); | |||
| 76 | /// Step the simulation loop. | 75 | /// Step the simulation loop. |
| 77 | /// | 76 | /// |
| 78 | /// The simulation always triggers a render of the initial state of simulation. | 77 | /// The simulation always triggers a render of the initial state of simulation. |
| 79 | void simloop_update(Simloop*, SimloopOut*); | 78 | void simloop_update(Simloop*, simloop_time_t dt, SimloopOut*); |
diff --git a/simloop/src/simloop.c b/simloop/src/simloop.c index 606f5ed..bd5a72d 100644 --- a/simloop/src/simloop.c +++ b/simloop/src/simloop.c | |||
| @@ -2,9 +2,9 @@ | |||
| 2 | 2 | ||
| 3 | #include <assert.h> | 3 | #include <assert.h> |
| 4 | 4 | ||
| 5 | static uint64_t ddt_from_fps(int fps) { | 5 | static simloop_time_t ddt_from_fps(int fps) { |
| 6 | static constexpr double NANOSECONDS = 1e9; | 6 | static constexpr double NANOSECONDS = 1e9; |
| 7 | return (fps == 0) ? 0 : (uint64_t)(NANOSECONDS / (double)fps); | 7 | return (fps == 0) ? 0 : (simloop_time_t)(NANOSECONDS / (double)fps); |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | Simloop simloop_make(const SimloopArgs* args) { | 10 | Simloop simloop_make(const SimloopArgs* args) { |
| @@ -12,31 +12,30 @@ Simloop simloop_make(const SimloopArgs* args) { | |||
| 12 | assert(args->update_fps > 0); | 12 | assert(args->update_fps > 0); |
| 13 | 13 | ||
| 14 | return (Simloop){ | 14 | return (Simloop){ |
| 15 | .frame = 0, | 15 | .frame = 0, |
| 16 | .update = (SimloopTimeline){.ddt = ddt_from_fps(args->update_fps), | 16 | .update = |
| 17 | .last_step = args->timer->start_time}, | 17 | (SimloopTimeline){ |
| 18 | .render = (SimloopTimeline){ .ddt = ddt_from_fps(args->max_render_fps), | 18 | .ddt = ddt_from_fps(args->update_fps), |
| 19 | .last_step = args->timer->start_time}, | 19 | .time = 0, |
| 20 | .timer = args->timer, | 20 | }, |
| 21 | .first_iter = true, | 21 | .render = |
| 22 | (SimloopTimeline){ | ||
| 23 | .ddt = ddt_from_fps(args->max_render_fps), | ||
| 24 | .time = 0, | ||
| 25 | }, | ||
| 26 | .first_iter = true, | ||
| 22 | .updates_since_last_render = false, | 27 | .updates_since_last_render = false, |
| 23 | }; | 28 | }; |
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | static time_delta time_elapsed(const Simloop* sim, time_point t) { | ||
| 27 | assert(sim); | ||
| 28 | return time_diff(sim->timer->start_time, t); | ||
| 29 | } | ||
| 30 | |||
| 31 | static bool step_update(const Simloop* sim, SimloopTimeline* timeline) { | 31 | static bool step_update(const Simloop* sim, SimloopTimeline* timeline) { |
| 32 | assert(sim); | 32 | assert(sim); |
| 33 | assert(timeline); | 33 | assert(timeline); |
| 34 | assert(timeline->ddt > 0); | 34 | assert(timeline->ddt > 0); |
| 35 | 35 | ||
| 36 | const time_delta dt = time_diff(timeline->last_step, sim->timer->last_tick); | 36 | const simloop_time_t dt = sim->clock - timeline->time; |
| 37 | const bool should_step = dt >= timeline->ddt; | 37 | const bool should_step = dt >= timeline->ddt; |
| 38 | timeline->last_step = | 38 | timeline->time += should_step ? timeline->ddt : 0; |
| 39 | should_step ? time_add(timeline->last_step, dt) : timeline->last_step; | ||
| 40 | return should_step; | 39 | return should_step; |
| 41 | } | 40 | } |
| 42 | 41 | ||
| @@ -48,32 +47,37 @@ static bool step_render(const Simloop* sim, SimloopTimeline* timeline) { | |||
| 48 | if (timeline->ddt > 0) { | 47 | if (timeline->ddt > 0) { |
| 49 | render = step_update(sim, timeline); | 48 | render = step_update(sim, timeline); |
| 50 | } else { | 49 | } else { |
| 51 | timeline->last_step = sim->timer->last_tick; | 50 | timeline->time = sim->clock; |
| 52 | render = true; | 51 | render = true; |
| 53 | } | 52 | } |
| 54 | return render; | 53 | return render; |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | void simloop_update(Simloop* sim, SimloopOut* out) { | 56 | void simloop_update(Simloop* sim, simloop_time_t dt, SimloopOut* out) { |
| 58 | assert(sim); | 57 | assert(sim); |
| 59 | assert(out); | 58 | assert(out); |
| 60 | 59 | ||
| 60 | sim->clock += dt; | ||
| 61 | |||
| 61 | // Simulation update. | 62 | // Simulation update. |
| 62 | const bool updated = step_update(sim, &sim->update); | 63 | const bool updated = step_update(sim, &sim->update); |
| 63 | out->should_update = updated; | ||
| 64 | sim->updates_since_last_render = sim->updates_since_last_render || updated; | 64 | sim->updates_since_last_render = sim->updates_since_last_render || updated; |
| 65 | |||
| 65 | // Simulation render. | 66 | // Simulation render. |
| 66 | const bool rendered = | 67 | const bool rendered = |
| 67 | (sim->updates_since_last_render && step_render(sim, &sim->render)) || | 68 | (sim->updates_since_last_render && step_render(sim, &sim->render)) || |
| 68 | (sim->first_iter); // Trigger an initial render on the first frame. | 69 | (sim->first_iter); // Trigger an initial render on the first frame. |
| 69 | out->should_render = rendered; | ||
| 70 | sim->updates_since_last_render = | 70 | sim->updates_since_last_render = |
| 71 | sim->updates_since_last_render && !out->should_render; | 71 | sim->updates_since_last_render && !out->should_render; |
| 72 | |||
| 72 | // Loop state update. | 73 | // Loop state update. |
| 73 | sim->frame += (updated ? 1 : 0); | 74 | sim->frame += (updated ? 1 : 0); |
| 74 | sim->first_iter = false; | 75 | sim->first_iter = false; |
| 76 | |||
| 75 | out->frame = sim->frame; | 77 | out->frame = sim->frame; |
| 76 | out->render_elapsed = time_elapsed(sim, sim->render.last_step); | 78 | out->render_elapsed = sim->render.time; |
| 77 | out->update_elapsed = time_elapsed(sim, sim->update.last_step); | 79 | out->update_elapsed = sim->update.time; |
| 78 | out->update_dt = sim->update.ddt; | 80 | out->update_dt = sim->update.ddt; |
| 81 | out->should_update = updated; | ||
| 82 | out->should_render = rendered; | ||
| 79 | } | 83 | } |
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,18 +1,25 @@ | |||
| 1 | #include <simloop.h> | 1 | #include <simloop.h> |
| 2 | 2 | ||
| 3 | #include <test.h> | 3 | #include <test.h> |
| 4 | #include <timer.h> | ||
| 5 | 4 | ||
| 6 | #include <stdint.h> | 5 | #include <stdint.h> |
| 7 | 6 | ||
| 8 | // ----------------------------------------------------------------------------- | 7 | // ----------------------------------------------------------------------------- |
| 8 | // Time. | ||
| 9 | |||
| 10 | static simloop_time_t time_delta_from_sec(double seconds) { | ||
| 11 | static constexpr double NANOS_PER_SEC = 1e9; | ||
| 12 | return (simloop_time_t)(seconds * NANOS_PER_SEC); | ||
| 13 | } | ||
| 14 | |||
| 15 | // ----------------------------------------------------------------------------- | ||
| 9 | // Randomness. | 16 | // Randomness. |
| 10 | 17 | ||
| 11 | typedef struct { | 18 | typedef struct { |
| 12 | uint64_t a; | 19 | uint64_t a; |
| 13 | } XorShift64State; | 20 | } XorShift64State; |
| 14 | 21 | ||
| 15 | uint64_t xorshift64(XorShift64State* state) { | 22 | static uint64_t xorshift64(XorShift64State* state) { |
| 16 | uint64_t x = state->a; | 23 | uint64_t x = state->a; |
| 17 | x ^= x << 7; | 24 | x ^= x << 7; |
| 18 | x ^= x >> 9; | 25 | x ^= x >> 9; |
| @@ -26,12 +33,10 @@ uint64_t xorshift64(XorShift64State* state) { | |||
| 26 | /// 1. An initial render is always triggered. | 33 | /// 1. An initial render is always triggered. |
| 27 | /// 2. No update is triggered (not enough time passed). | 34 | /// 2. No update is triggered (not enough time passed). |
| 28 | TEST_CASE(simloop_initial_render) { | 35 | TEST_CASE(simloop_initial_render) { |
| 29 | Timer timer = {}; | 36 | Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = 10}); |
| 30 | Simloop simloop = | ||
| 31 | simloop_make(&(SimloopArgs){.update_fps = 10, .timer = &timer}); | ||
| 32 | SimloopOut simout; | 37 | SimloopOut simout; |
| 33 | 38 | ||
| 34 | simloop_update(&simloop, &simout); | 39 | simloop_update(&simloop, 0, &simout); |
| 35 | 40 | ||
| 36 | TEST_TRUE(simout.should_render); | 41 | TEST_TRUE(simout.should_render); |
| 37 | TEST_TRUE(!simout.should_update); | 42 | TEST_TRUE(!simout.should_update); |
| @@ -41,12 +46,11 @@ TEST_CASE(simloop_initial_render) { | |||
| 41 | /// The initial render is not re-triggered if there is a render frame rate cap | 46 | /// The initial render is not re-triggered if there is a render frame rate cap |
| 42 | /// and time does not advance. | 47 | /// and time does not advance. |
| 43 | TEST_CASE(simloop_initial_render_not_retriggered) { | 48 | TEST_CASE(simloop_initial_render_not_retriggered) { |
| 44 | Timer timer = {}; | 49 | Simloop simloop = |
| 45 | Simloop simloop = simloop_make( | 50 | simloop_make(&(SimloopArgs){.update_fps = 10, .max_render_fps = 10}); |
| 46 | &(SimloopArgs){.update_fps = 10, .max_render_fps = 10, .timer = &timer}); | ||
| 47 | SimloopOut simout; | 51 | SimloopOut simout; |
| 48 | 52 | ||
| 49 | simloop_update(&simloop, &simout); | 53 | simloop_update(&simloop, 0, &simout); |
| 50 | 54 | ||
| 51 | TEST_TRUE(simout.should_render); | 55 | TEST_TRUE(simout.should_render); |
| 52 | TEST_TRUE(!simout.should_update); | 56 | TEST_TRUE(!simout.should_update); |
| @@ -54,7 +58,7 @@ TEST_CASE(simloop_initial_render_not_retriggered) { | |||
| 54 | 58 | ||
| 55 | for (int i = 0; i < 10; i++) { | 59 | for (int i = 0; i < 10; i++) { |
| 56 | // Note that time does not advance. | 60 | // Note that time does not advance. |
| 57 | simloop_update(&simloop, &simout); | 61 | simloop_update(&simloop, 0, &simout); |
| 58 | TEST_TRUE(!simout.should_render); | 62 | TEST_TRUE(!simout.should_render); |
| 59 | TEST_TRUE(!simout.should_update); | 63 | TEST_TRUE(!simout.should_update); |
| 60 | TEST_EQUAL(simout.frame, 0); | 64 | TEST_EQUAL(simout.frame, 0); |
| @@ -65,27 +69,27 @@ TEST_CASE(simloop_initial_render_not_retriggered) { | |||
| 65 | /// 1. Updates based on the desired update frame rate. | 69 | /// 1. Updates based on the desired update frame rate. |
| 66 | /// 2. Renders at every loop (provided there are updates). | 70 | /// 2. Renders at every loop (provided there are updates). |
| 67 | TEST_CASE(simloop_no_render_frame_cap) { | 71 | TEST_CASE(simloop_no_render_frame_cap) { |
| 68 | constexpr int UPDATE_FPS = 10; // 100ms delta | 72 | constexpr int UPDATE_FPS = 10; // 100ms delta |
| 69 | const time_delta UPDATE_DDT = sec_to_time_delta(1.0 / (double)UPDATE_FPS); | 73 | const simloop_time_t UPDATE_DDT = |
| 70 | const time_delta STEP = sec_to_time_delta(1); | 74 | time_delta_from_sec(1.0 / (double)UPDATE_FPS); |
| 71 | const time_delta SIM_TIME_SEC = sec_to_time_delta(30); | 75 | const simloop_time_t STEP = time_delta_from_sec(1); |
| 76 | const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(30); | ||
| 72 | 77 | ||
| 73 | // We need simulation time to be an exact multiple of the desired deltas for | 78 | // We need simulation time to be an exact multiple of the desired deltas for |
| 74 | // the modulo comparisons below. | 79 | // the modulo comparisons below. |
| 75 | TEST_TRUE((STEP % UPDATE_DDT) == 0); | 80 | TEST_TRUE((STEP % UPDATE_DDT) == 0); |
| 76 | 81 | ||
| 77 | Timer timer = {}; | 82 | simloop_time_t dt = 0; |
| 78 | Simloop simloop = | 83 | Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS}); |
| 79 | simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS, .timer = &timer}); | ||
| 80 | SimloopOut simout; | 84 | SimloopOut simout; |
| 81 | 85 | ||
| 82 | for (time_delta t = 0; t < SIM_TIME_SEC; t += STEP) { | 86 | for (simloop_time_t t = 0; t <= SIM_TIME_SEC; t += STEP) { |
| 83 | timer_advance(&timer, t); | 87 | simloop_update(&simloop, dt, &simout); |
| 84 | simloop_update(&simloop, &simout); | ||
| 85 | const bool expect_update = (t > 0) && ((t % UPDATE_DDT) == 0); | 88 | const bool expect_update = (t > 0) && ((t % UPDATE_DDT) == 0); |
| 86 | // A render is still expected at time 0. | 89 | // A render is still expected at time 0. |
| 87 | TEST_EQUAL(simout.should_render, (t == 0) || expect_update); | 90 | TEST_EQUAL(simout.should_render, (t == 0) || expect_update); |
| 88 | TEST_EQUAL(simout.should_update, expect_update); | 91 | TEST_EQUAL(simout.should_update, expect_update); |
| 92 | dt = STEP; | ||
| 89 | } | 93 | } |
| 90 | } | 94 | } |
| 91 | 95 | ||
| @@ -93,29 +97,31 @@ TEST_CASE(simloop_no_render_frame_cap) { | |||
| 93 | /// 1. Updates based on the desired update frame rate. | 97 | /// 1. Updates based on the desired update frame rate. |
| 94 | /// 2. Renders based on the desired render frame rate. | 98 | /// 2. Renders based on the desired render frame rate. |
| 95 | TEST_CASE(simloop_with_render_frame_cap) { | 99 | TEST_CASE(simloop_with_render_frame_cap) { |
| 96 | constexpr int UPDATE_FPS = 10; // 100ms delta | 100 | constexpr int UPDATE_FPS = 10; // 100ms delta |
| 97 | constexpr int RENDER_FPS = 5; // 200ms delta | 101 | constexpr int RENDER_FPS = 5; // 200ms delta |
| 98 | const time_delta UPDATE_DDT = sec_to_time_delta(1.0 / (double)UPDATE_FPS); | 102 | const simloop_time_t UPDATE_DDT = |
| 99 | const time_delta RENDER_DDT = sec_to_time_delta(1.0 / (double)RENDER_FPS); | 103 | time_delta_from_sec(1.0 / (double)UPDATE_FPS); |
| 100 | const time_delta STEP = sec_to_time_delta(0.1); // 100ms | 104 | const simloop_time_t RENDER_DDT = |
| 101 | const time_delta SIM_TIME_SEC = sec_to_time_delta(30); | 105 | time_delta_from_sec(1.0 / (double)RENDER_FPS); |
| 106 | const simloop_time_t STEP = time_delta_from_sec(0.1); // 100ms | ||
| 107 | const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(30); | ||
| 102 | 108 | ||
| 103 | // We need simulation time to be an exact multiple of the desired deltas for | 109 | // We need simulation time to be an exact multiple of the desired deltas for |
| 104 | // the modulo comparisons below. | 110 | // the modulo comparisons below. |
| 105 | TEST_TRUE((UPDATE_DDT % STEP) == 0); | 111 | TEST_TRUE((UPDATE_DDT % STEP) == 0); |
| 106 | TEST_TRUE((RENDER_DDT % STEP) == 0); | 112 | TEST_TRUE((RENDER_DDT % STEP) == 0); |
| 107 | 113 | ||
| 108 | Timer timer = {}; | 114 | simloop_time_t dt = 0; |
| 109 | Simloop simloop = simloop_make(&(SimloopArgs){ | 115 | Simloop simloop = simloop_make( |
| 110 | .update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS, .timer = &timer}); | 116 | &(SimloopArgs){.update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS}); |
| 111 | SimloopOut simout; | 117 | SimloopOut simout; |
| 112 | 118 | ||
| 113 | for (time_delta t = 0; t < SIM_TIME_SEC; t += STEP) { | 119 | for (simloop_time_t t = 0; t <= SIM_TIME_SEC; t += STEP) { |
| 114 | timer_advance(&timer, t); | 120 | simloop_update(&simloop, dt, &simout); |
| 115 | simloop_update(&simloop, &simout); | ||
| 116 | // A render is still expected at time 0. | 121 | // A render is still expected at time 0. |
| 117 | TEST_EQUAL(simout.should_render, (t % RENDER_DDT) == 0); | 122 | TEST_EQUAL(simout.should_render, (t % RENDER_DDT) == 0); |
| 118 | TEST_EQUAL(simout.should_update, (t > 0) && ((t % UPDATE_DDT) == 0)); | 123 | TEST_EQUAL(simout.should_update, (t > 0) && ((t % UPDATE_DDT) == 0)); |
| 124 | dt = STEP; | ||
| 119 | } | 125 | } |
| 120 | } | 126 | } |
| 121 | 127 | ||
| @@ -130,16 +136,16 @@ TEST_CASE(simloop_with_render_frame_cap) { | |||
| 130 | /// the client to decide whether to update just once or as many times as | 136 | /// the client to decide whether to update just once or as many times as |
| 131 | /// requested, depending on whether they want determinism or convergence. | 137 | /// requested, depending on whether they want determinism or convergence. |
| 132 | TEST_CASE(simloop_determinism) { | 138 | TEST_CASE(simloop_determinism) { |
| 133 | constexpr int UPDATE_FPS = 100; // 10ms delta | 139 | constexpr int UPDATE_FPS = 100; // 10ms delta |
| 134 | const time_delta RANDOM_STEPS[] = { | 140 | const simloop_time_t RANDOM_STEPS[] = { |
| 135 | sec_to_time_delta(0.007), // 7ms | 141 | time_delta_from_sec(0.007), // 7ms |
| 136 | sec_to_time_delta(0.005), // 5ms | 142 | time_delta_from_sec(0.005), // 5ms |
| 137 | sec_to_time_delta(0.003), // 3ms | 143 | time_delta_from_sec(0.003), // 3ms |
| 138 | }; | 144 | }; |
| 139 | constexpr uint64_t NUM_RANDOM_STEPS = | 145 | constexpr uint64_t NUM_RANDOM_STEPS = |
| 140 | sizeof(RANDOM_STEPS) / sizeof(RANDOM_STEPS[0]); | 146 | sizeof(RANDOM_STEPS) / sizeof(RANDOM_STEPS[0]); |
| 141 | const time_delta SIM_TIME_SEC = sec_to_time_delta(10); | 147 | const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(10); |
| 142 | constexpr float ADD = 0.123f; | 148 | constexpr float ADD = 0.123f; |
| 143 | 149 | ||
| 144 | typedef struct Simulation { | 150 | typedef struct Simulation { |
| 145 | int iter_count; | 151 | int iter_count; |
| @@ -153,26 +159,26 @@ TEST_CASE(simloop_determinism) { | |||
| 153 | } | 159 | } |
| 154 | 160 | ||
| 155 | Simulation sim[2] = {0}; | 161 | Simulation sim[2] = {0}; |
| 156 | XorShift64State xss = (XorShift64State){12069019817132197873}; | 162 | XorShift64State xss = (XorShift64State){12069019817132197873ULL}; |
| 157 | 163 | ||
| 158 | // Perform two simulations with random clock-time steps. | 164 | // Perform two simulations with random clock-time steps. |
| 159 | for (int s = 0; s < 2; ++s) { | 165 | for (int s = 0; s < 2; ++s) { |
| 160 | Timer timer = {}; | 166 | simloop_time_t dt = 0; |
| 161 | Simloop simloop = | 167 | Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS}); |
| 162 | simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS, .timer = &timer}); | ||
| 163 | SimloopOut simout; | 168 | SimloopOut simout; |
| 164 | 169 | ||
| 165 | for (time_delta t = 0; t < SIM_TIME_SEC;) { | 170 | for (simloop_time_t t = 0; t <= SIM_TIME_SEC;) { |
| 166 | timer_advance(&timer, t); | 171 | simloop_update(&simloop, dt, &simout); |
| 167 | simloop_update(&simloop, &simout); | ||
| 168 | 172 | ||
| 169 | if (simout.should_update) { | 173 | if (simout.should_update) { |
| 170 | UPDATE_SIMULATION(sim[s]); | 174 | UPDATE_SIMULATION(sim[s]); |
| 171 | } | 175 | } |
| 172 | 176 | ||
| 173 | // Advance time with a random step. | 177 | // Advance time with a random step. |
| 174 | const time_delta step = RANDOM_STEPS[xorshift64(&xss) % NUM_RANDOM_STEPS]; | 178 | const simloop_time_t step = |
| 179 | RANDOM_STEPS[xorshift64(&xss) % NUM_RANDOM_STEPS]; | ||
| 175 | t += step; | 180 | t += step; |
| 181 | dt = step; | ||
| 176 | } | 182 | } |
| 177 | } | 183 | } |
| 178 | 184 | ||
