1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
/* Simulation loop module.
*
* This implements a simulation loop but in a way that the client retains
* control flow. The client steps the loop and then checks whether the
* simulation must be updated and/or the result rendered.
*
* If the simulation's update cannot keep up with the desired frame rate, then
* the loop degrades to match the simulation's rate by requesting a single
* update.
*
* Under a variable time delta, the loop could simply update the simulation
* with a large delta that puts the simulation back into the current clock
* time. Under a fixed time delta this isn't possible, and we seem to have two
* choices instead:
*
* a) Queue as many updates as necessary to bring the simulation back to the
* current clock time (time_diff / fixed_delta).
*
* b) Queue a single update.
*
* The issue with (a) is that the number of requested updates diverges and
* eventually the simulation appears to freeze. At every loop, the number of
* queued updates increases with respect to the last iteration as the
* simulation fails to keep up with the desired frame rate. Example:
*
* desired delta = 10ms (100 fps)
* actual delta = 20ms ( 50 fps)
* ---------------------------
* iter, sim time, clock time
* ---------------------------
* 0, 0, 0, initial state
* 1, 0, 10, queue 1 update
* 2, 10, 30, queue (30-10)/10 = 2 updates
* 3, 30, 70, queue (70-30)/10 = 4 updates
* 4, 70, 150, queue (150-70)/10 = 8 updates
* ...
*/
#pragma once
#include <timer.h>
#include <stdint.h>
typedef struct SimloopArgs {
int update_fps; ///< Update frame rate. Must be >0.
int max_render_fps; ///< Render frame rate cap. 0 to disable.
Timer* timer; ///< Timer that drives the simulation.
} SimloopArgs;
typedef struct SimloopOut {
uint64_t frame; ///< Frame counter.
time_delta render_elapsed; ///< Amount of time elapsed in the rendering.
time_delta update_elapsed; ///< Amount of time elapsed in the simulation.
time_delta update_dt; ///< Delta time for simulation updates.
bool should_update; ///< Whether the simulation should update.
bool should_render; ///< Whether the simulation should be rendered.
} SimloopOut;
typedef struct SimloopTimeline {
time_delta ddt; ///< Desired delta time.
time_point last_step; ///< Time of the last simulation step.
} SimloopTimeline;
typedef struct Simloop {
SimloopTimeline update; ///< Update timeline.
SimloopTimeline render; ///< Render timeline.
uint64_t frame; ///< Frame counter.
Timer* timer;
bool first_iter;
bool updates_since_last_render;
} Simloop;
/// Create a simulation loop.
Simloop simloop_make(const SimloopArgs*);
/// Step the simulation loop.
///
/// The simulation always triggers a render of the initial state of simulation.
void simloop_update(Simloop*, SimloopOut*);
|