aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-12-22 09:01:12 -0800
committer3gg <3gg@shellblade.net>2024-12-22 09:01:12 -0800
commitb7e420f96e5649ebc6bbbfc012fd002e5069fefa (patch)
tree7c9f60c0caecaa69b7cba0d9d7a000afc03cb9d5
parent8828c4be56430c641d26f83aca8950c7419b587c (diff)
Use fixed time points in animation.
-rw-r--r--Spear/App.hs29
-rw-r--r--Spear/Sys/Timer.hsc13
-rw-r--r--Spear/Sys/Timer/timer.c9
-rw-r--r--Spear/Sys/Timer/timer.h3
4 files changed, 42 insertions, 12 deletions
diff --git a/Spear/App.hs b/Spear/App.hs
index f8afc9e..ac5f7a5 100644
--- a/Spear/App.hs
+++ b/Spear/App.hs
@@ -59,50 +59,55 @@ loop app window = do
59 resizeApp app (ResizeEvent width height) 59 resizeApp app (ResizeEvent width height)
60 renderApp app 60 renderApp app
61 61
62 let ddt = fpsToDdt . maxFPS . options $ app -- Desired frame delta time. 62 let ddt = fpsToDdt . maxFPS . options $ app -- Desired render time step.
63 let animationDdt = fpsToDdt . animationFPS . options $ app -- Desired animation time step. 63 let animationDdt = fpsToDdt . animationFPS . options $ app -- Desired animation time step.
64 64
65 timer <- gameIO newTimer 65 timer <- gameIO newTimer
66 gameIO $ Timer.start timer 66 gameIO $ Timer.start timer
67 loop' window ddt animationDdt 0 timer app 67 let lastAnimationTime = lastTick timer
68 loop' window ddt animationDdt lastAnimationTime timer app
68 69
69loop' :: 70loop' ::
70 Window -> 71 Window ->
71 TimeDelta -> -- Desired frame time delta. 72 TimeDelta -> -- Desired frame time delta.
72 TimeDelta -> -- Desired animation time delta. 73 TimeDelta -> -- Desired animation time delta.
73 TimeDelta -> -- Time budget. 74 TimePoint -> -- Time point of last animation update.
74 Timer -> 75 Timer ->
75 App s -> 76 App s ->
76 Game s () 77 Game s ()
77loop' window ddt animationDdt timeBudget inputTimer app = do 78loop' window ddt animationDdt lastAnimationTime inputTimer app = do
78 timer <- gameIO $ tick inputTimer 79 timer <- gameIO $ tick inputTimer
79 windowEvents <- gameIO $ pollWindowEvents window 80 windowEvents <- gameIO $ pollWindowEvents window
80 close <- gameIO $ shouldWindowClose window 81 close <- gameIO $ shouldWindowClose window
81 82
82 (continue, timeBudgetNextFrame) <- case animationDdt of 83 (continue, lastAnimationTimeNextFrame) <- case animationDdt of
83 0 -> do 84 0 -> do
84 -- Variable time step game animation. 85 -- Variable time step game animation.
85 let t = timeDeltaToSec $ runningTime timer 86 let t = timeDeltaToSec $ runningTime timer
86 let dt = timeDeltaToSec $ deltaTime timer 87 let dt = timeDeltaToSec $ deltaTime timer
87 inputEvents <- gameIO $ pollInputEvents window 88 inputEvents <- gameIO $ pollInputEvents window
88 continue <- stepApp app t dt inputEvents 89 continue <- stepApp app t dt inputEvents
89 return (continue, 0) -- budget unused. 90 return (continue, lastAnimationTime)
90 91
91 _ -> do 92 _ -> do
92 -- Fixed time step animation. 93 -- Fixed time step animation.
93 let elapsed = runningTime timer 94 {- let elapsed = runningTime timer
94 let dt = timeDeltaToSec ddt 95 let dt = timeDeltaToSec ddt
95 let timeBudgetThisFrame = timeBudget + deltaTime timer 96 let timeBudgetThisFrame = timeBudget + deltaTime timer
96 let timeBudgetNextFrame = timeBudgetThisFrame `mod` ddt 97 let timeBudgetNextFrame = timeBudgetThisFrame `mod` ddt
98 let steps = timeBudgetThisFrame `div` ddt -}
99 --gameIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt
100 let elapsed = runningTime timer
101 let dt = timeDeltaToSec ddt
102 let timeBudgetThisFrame = timeDiff lastAnimationTime (lastTick timer)
97 let steps = timeBudgetThisFrame `div` ddt 103 let steps = timeBudgetThisFrame `div` ddt
98 gameIO . print $ steps 104 let lastAnimationTimeNextFrame = timeAdd lastAnimationTime (timeBudgetThisFrame `mod` ddt)
99 105 --gameIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt
100 continue <- and <$> forM [1..steps] (\i -> do 106 continue <- and <$> forM [1..steps] (\i -> do
101 inputEvents <- gameIO $ pollInputEvents window 107 inputEvents <- gameIO $ pollInputEvents window
102 let t = timeDeltaToSec $ elapsed + i * ddt 108 let t = timeDeltaToSec $ elapsed + i * ddt
103 stepApp app t dt inputEvents) 109 stepApp app t dt inputEvents)
104 110 return (continue, lastAnimationTimeNextFrame)
105 return (continue, timeBudgetNextFrame)
106 111
107 -- Process window events. 112 -- Process window events.
108 resized <- or <$> forM windowEvents (\event -> case event of 113 resized <- or <$> forM windowEvents (\event -> case event of
@@ -121,4 +126,4 @@ loop' window ddt animationDdt timeBudget inputTimer app = do
121 gameIO $ Timer.sleep (ddt - frameTime) 126 gameIO $ Timer.sleep (ddt - frameTime)
122 127
123 when (continue && not close) $ do 128 when (continue && not close) $ do
124 loop' window ddt animationDdt timeBudgetNextFrame timer app 129 loop' window ddt animationDdt lastAnimationTimeNextFrame timer app
diff --git a/Spear/Sys/Timer.hsc b/Spear/Sys/Timer.hsc
index 98b88d6..fb18521 100644
--- a/Spear/Sys/Timer.hsc
+++ b/Spear/Sys/Timer.hsc
@@ -14,6 +14,7 @@ module Spear.Sys.Timer
14, timeDeltaToSec 14, timeDeltaToSec
15, secToTimeDelta 15, secToTimeDelta
16, timePointToNs 16, timePointToNs
17, timeAdd
17, sleep 18, sleep
18) 19)
19where 20where
@@ -115,6 +116,9 @@ foreign import ccall safe "timer.h sec_to_time_delta"
115foreign import ccall safe "timer.h time_point_to_ns" 116foreign import ccall safe "timer.h time_point_to_ns"
116 c_time_point_to_ns :: Ptr TimePoint -> Word64 117 c_time_point_to_ns :: Ptr TimePoint -> Word64
117 118
119foreign import ccall safe "timer.h time_add"
120 c_time_add :: Ptr TimePoint -> TimeDelta -> Ptr TimePoint -> IO ()
121
118foreign import ccall "timer.h time_sleep" 122foreign import ccall "timer.h time_sleep"
119 c_time_sleep :: TimeDelta -> IO () 123 c_time_sleep :: TimeDelta -> IO ()
120 124
@@ -172,6 +176,15 @@ timePointToNs t = unsafeDupablePerformIO $ alloca $ \ptr -> do
172 poke ptr t 176 poke ptr t
173 return $ c_time_point_to_ns ptr 177 return $ c_time_point_to_ns ptr
174 178
179-- | Add a time delta to a timestamp.
180timeAdd :: TimePoint -> TimeDelta -> TimePoint
181timeAdd t dt = unsafeDupablePerformIO $
182 alloca $ \tPtr ->
183 alloca $ \ptr -> do
184 poke tPtr t
185 c_time_add tPtr dt ptr
186 peek ptr
187
175-- | Put the caller thread to sleep for the given amount of time. 188-- | Put the caller thread to sleep for the given amount of time.
176sleep :: TimeDelta -> IO () 189sleep :: TimeDelta -> IO ()
177sleep = c_time_sleep 190sleep = c_time_sleep
diff --git a/Spear/Sys/Timer/timer.c b/Spear/Sys/Timer/timer.c
index 707cd63..4a2fb2b 100644
--- a/Spear/Sys/Timer/timer.c
+++ b/Spear/Sys/Timer/timer.c
@@ -87,6 +87,15 @@ uint64_t time_point_to_ns(time_point* t) {
87#endif 87#endif
88} 88}
89 89
90void time_add(const time_point* t, time_delta dt, time_point* out) {
91#ifdef _WIN32
92 *out = *t + dt;
93#else
94 out->tv_sec = t->tv_sec + (dt / nanoseconds);
95 out->tv_nsec = t->tv_nsec + (dt % nanoseconds);
96#endif
97}
98
90void time_sleep(time_delta dt) { 99void time_sleep(time_delta dt) {
91#ifdef _WIN32 100#ifdef _WIN32
92 const int64_t ms = dt / microseconds; 101 const int64_t ms = dt / microseconds;
diff --git a/Spear/Sys/Timer/timer.h b/Spear/Sys/Timer/timer.h
index e426135..da4e7c7 100644
--- a/Spear/Sys/Timer/timer.h
+++ b/Spear/Sys/Timer/timer.h
@@ -53,6 +53,9 @@ time_delta sec_to_time_delta(double seconds);
53/// Convert the time point to nanoseconds. 53/// Convert the time point to nanoseconds.
54uint64_t time_point_to_ns(time_point*); 54uint64_t time_point_to_ns(time_point*);
55 55
56/// Add a time delta to a timestamp.
57void time_add(const time_point*, time_delta, time_point* out);
58
56/// Put the caller thread to sleep for the given amount of time. 59/// Put the caller thread to sleep for the given amount of time.
57void time_sleep(time_delta dt); 60void time_sleep(time_delta dt);
58 61