Title: Timing in Real Time Simulations
1Timing in Real Time Simulations
- 11/11/09
- Kevin Dressel
- dressel3
2Moving an Object
On your machine
3Moving an Object
On an older machine
4Moving an Object
On machine in future
5Common Way to Do It
- Update(float dt)
-
- obj.pos (obj.vel dt)
- dt is the time since the last update (variable)
- Change position in proportion to the speed the
simulation is running at
6Common Way to Do It
- Incrementally integrate over time
- (Euler method)
- Variable time steps
- Like Riemann Sum
Update(float dt) obj.pos (obj.vel dt)
Velocity
dt
Position
7Stability
- This is an estimation
- Variable time steps
- Large dt makes incremental integration inaccurate
- Complex physics tends to be very sensitive to
this
8Solutions
- Find better integration method
- Ignore large jumps in dt
- Go back to fixed time step
- Ignore it
- One common way to fix this
- Use fixed time step for physics
- If running too quickly, wait until fixed time
passed to update - Or, do as many physics steps needed to catch up
to current time
9Example
Running too slow Do as many physics steps as
needed to catch up to current time. Running too
fast Dont update physics until FIXED_DT time
has actually passed
- define FIXED_DT (0.017f)
- float remainder 0.0f
- Update(float dt)
-
- remainder dt
- while(remainder gt FIXED_DT)
-
- obj.vel obj.acc FIXED_DT
- obj.pos obj.vel FIXED_DT
- remainder - FIXED_DT
-
10Example
- define FIXED_DT (0.017f)
- define MAX_DT (2.0f)
- float remainder 0.0f
- Update(float dt)
-
- if(dt gt MAX_DT) return
- remainder dt
- while(remainder gt FIXED_DT)
-
- obj.vel obj.acc FIXED_DT
- obj.pos obj.vel FIXED_DT
- remainder - FIXED_DT
-
To avoid getting stuck in the while loop, skip
this update if dt is too big.
11What Does it Look Like?
Physics Timeline
0 steps
1 step
2 steps
3 steps
4 steps
Simulation Timeline
12Update 0
13Update 1
14Update 2
15Update 3
16Update 4
17Update 5
18Update 6
19Example
- define FIXED_DT (0.017f)
- define MAX_DT (2.0f)
- float remainder 0.0f
- Update(float dt)
-
- if(dt gt MAX_DT) return
- remainder dt
- while(remainder gt FIXED_DT)
-
- obj.old.pos obj.curr.pos
- obj.old.vel obj.curr.vel
- obj.curr.vel obj.curr.acc FIXED_DT
- obj.curr.pos obj.curr.vel FIXED_DT
- remainder - FIXED_DT
-
-
- Keep previous physics state and a current physics
state. - Interpolate between them based on how far into
the next physics state we are
Linear interpolation
20Update 0 with Interpolation
21Update 1 with Interpolation
22Update 2 with Interpolation
23Update 3 with Interpolation
24Update 4 with Interpolation
25Update 5 with Interpolation
26Update 6 with Interpolation
27Timer Resolution
- Time between updates is extremely small
- Real time requires high resolution time queries
- time() 1 second resolution
- GetTickCount() clock() 25-60 millisecond
resolution - timeGetTime() 15-30 millisecond resolution
- Microsecond resolution (run in separate thread)
- X86 rdtsc instruction
- QueryPerformanceCounter on Windows
- gettimeofday() (µs) and clock_gettime() (ns) on
Linux
28From SDL for Linux
void SDL_StartTicks(void) / Set first ticks
value / if HAVE_CLOCK_GETTIME clock_gettime(CLO
CK_MONOTONIC,start) else gettimeofday(start,
NULL) endif Uint32 SDL_GetTicks (void) if
HAVE_CLOCK_GETTIME Uint32 ticks struct
timespec now clock_gettime(CLOCK_MONOTONIC,now)
ticks(now.tv_sec-start.tv_sec)1000(now.tv_ns
ec-start.tv_nsec)/1000000 return(ticks) else
Uint32 ticks struct timeval now gettimeofday(
now, NULL) ticks(now.tv_sec-start.tv_sec)1000
(now.tv_usec-start.tv_usec)/1000 return(ticks)
endif
29From SDL for Windows
void SDL_StartTicks(void) / Set first ticks
value / ifdef USE_GETTICKCOUNT start
GetTickCount() else if (QueryPerformanceFrequen
cy(hires_ticks_per_second) TRUE) hires_ti
mer_available TRUE QueryPerformanceCounter(h
ires_start_ticks) else hires_timer_avail
able FALSE timeBeginPeriod(1) / use 1 ms
timer precision / start timeGetTime() en
dif
30From SDL for Windows
Uint32 SDL_GetTicks(void) DWORD now,
ticks ifndef USE_GETTICKCOUNT LARGE_INTEGER
hires_now endif ifdef USE_GETTICKCOUNT now
GetTickCount() else if (hires_timer_available)
QueryPerformanceCounter(hires_now) hires_
now.QuadPart - hires_start_ticks.QuadPart hire
s_now.QuadPart 1000 hires_now.QuadPart /
hires_ticks_per_second.QuadPart return
(DWORD)hires_now.QuadPart else now
timeGetTime() endif if ( now lt start )
ticks (TIME_WRAP_VALUE-start) now
else ticks (now - start) return(ticks)
31Conclusion
- We want simulations to run predictably on any
machine - Integrators are sensitive to large/variable dt
- Physics is sensitive to inaccurate integration
- Need to make sure timer can update fast enough