aboutsummaryrefslogtreecommitdiff
path: root/simloop/README.md
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2026-04-26 13:06:44 -0700
committer3gg <3gg@shellblade.net>2026-04-26 13:06:44 -0700
commit48422e313b31b79d76dd8f027b4d934994168859 (patch)
tree8235fe036e556ee3810d0a57846be7f666b1a894 /simloop/README.md
parente014d3cc269c636767528f2cdd716fc6badcaa89 (diff)
Test for catch-up. Document
Diffstat (limited to 'simloop/README.md')
-rw-r--r--simloop/README.md99
1 files changed, 99 insertions, 0 deletions
diff --git a/simloop/README.md b/simloop/README.md
new file mode 100644
index 0000000..f45ca05
--- /dev/null
+++ b/simloop/README.md
@@ -0,0 +1,99 @@
1# Simulation loop module
2
3Simulation loop for games and graphics applications.
4
5## Features
6
7- Client retains control flow.
8- Client-controlled time axis.
9- Updates are frame-rate capped and use a fixed time step for determinism.
10- Rendering is optionally frame-rate capped.
11- Interpolation factor for smooth animation and rendering between frames.
12
13Control flow: the client steps the loop and then checks whether the simulation
14must be updated and/or the result rendered. Time readings are external to the
15library and provided by the client.
16
17## Invariants
18
19- An initial render of the initial application state is always triggered.
20- The same frame is not re-rendered if time does not advance.
21- Animation interpolation factor in [0,1].
22
23## Handling Time Spikes
24
25Generally, the simulation's update logic should be able to keep up with the
26requested frame rate; it is the application's responsibility to ensure this.
27Specifically, the frequency with which the application loops must be higher
28than the requested update frequency, given by the update delta time.
29
30However, occasional time spikes may occur, for example when switching to the
31desktop or when pausing the application in a debugger. The library handles this
32simply by requesting an update from the application. Under the assumption that
33the loop frequency is higher than the update frequency, the simulation will
34catch up with the real-time clock.
35
36### Time Spikes in Detail
37
38When a time spike occurs, the simulation clock falls significantly behind the
39real-time clock. Ideally, the simulation should be able to recover and catch up
40to the real-time clock when this occurs.
41
42Under a variable time delta, the loop could simply update the simulation with
43a large delta that puts the simulation back into the current clock time.
44Under a fixed time delta, this isn't possible, and we seem to have the
45following choices instead:
46
47- a) Queue as many updates as necessary to bring the simulation back to the
48 current clock time (time_difference / fixed_delta).
49- b) Queue a single update.
50- c) Some middle ground between the two.
51
52The issue with (a) is that, if the simulation is never able to catch up, then
53the number of requested updates at every loop iteration diverges and the
54simulation eventually appears to freeze.
55
56(b) only works if:
57
58- clock time added per iter < desired update delta time
59
60Where:
61
62- clock time added per iter = update time + render time + vsync + etc
63- desired delta time = 1 / update frequency
64
65If the clock time added per iteration is greater or equal to the desired delta,
66then the simulation can never "catch up" and recover from the spike.
67
68The middle ground is to perform only some number of updates in each loop
69iteration N. The simulation catches up only if:
70
71- clock time added per iter < N * desired update delta time
72
73The ideal value of N depends on how many frames the application can actually
74render. For example, if the application is vsync'ed to a 240hz monitor and is
75able to render that many frames, then:
76
77- N = ceil(1/240hz / desired update delta time)
78
79Realistically, the actual frame rate will be variable. Moreover, if we queued
80as many frames as possible, then we would risk the freeze in option (a) if the
81actual update time were too large for the application to catch up. So the
82library can only guess the value of N.
83
84The library picks a small constant value of N, implementation-defined, that the
85application can override.
86
87### Example: Spike Handling with Option (A)
88
89- desired delta = 10ms (100 fps)
90- actual delta = 20ms ( 50 fps)
91
92| iter | sim time | clock time | comment |
93|------|----------|------------|-------------------------------|
94| 0 | 0 | 0 | initial state |
95| 1 | 0 | 10 | queue 1 update |
96| 2 | 10 | 30 | queue (30-10)/10 = 2 updates |
97| 3 | 30 | 70 | queue (70-30)/10 = 4 updates |
98| 4 | 70 | 150 | queue (150-70)/10 = 8 updates |
99| ... |