diff options
| author | 3gg <3gg@shellblade.net> | 2024-12-22 09:01:12 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2024-12-22 09:01:12 -0800 |
| commit | b7e420f96e5649ebc6bbbfc012fd002e5069fefa (patch) | |
| tree | 7c9f60c0caecaa69b7cba0d9d7a000afc03cb9d5 | |
| parent | 8828c4be56430c641d26f83aca8950c7419b587c (diff) | |
Use fixed time points in animation.
| -rw-r--r-- | Spear/App.hs | 29 | ||||
| -rw-r--r-- | Spear/Sys/Timer.hsc | 13 | ||||
| -rw-r--r-- | Spear/Sys/Timer/timer.c | 9 | ||||
| -rw-r--r-- | Spear/Sys/Timer/timer.h | 3 |
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 | ||
| 69 | loop' :: | 70 | loop' :: |
| 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 () |
| 77 | loop' window ddt animationDdt timeBudget inputTimer app = do | 78 | loop' 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 | ) |
| 19 | where | 20 | where |
| @@ -115,6 +116,9 @@ foreign import ccall safe "timer.h sec_to_time_delta" | |||
| 115 | foreign import ccall safe "timer.h time_point_to_ns" | 116 | foreign 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 | ||
| 119 | foreign import ccall safe "timer.h time_add" | ||
| 120 | c_time_add :: Ptr TimePoint -> TimeDelta -> Ptr TimePoint -> IO () | ||
| 121 | |||
| 118 | foreign import ccall "timer.h time_sleep" | 122 | foreign 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. | ||
| 180 | timeAdd :: TimePoint -> TimeDelta -> TimePoint | ||
| 181 | timeAdd 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. |
| 176 | sleep :: TimeDelta -> IO () | 189 | sleep :: TimeDelta -> IO () |
| 177 | sleep = c_time_sleep | 190 | sleep = 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 | ||
| 90 | void 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 | |||
| 90 | void time_sleep(time_delta dt) { | 99 | void 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. |
| 54 | uint64_t time_point_to_ns(time_point*); | 54 | uint64_t time_point_to_ns(time_point*); |
| 55 | 55 | ||
| 56 | /// Add a time delta to a timestamp. | ||
| 57 | void 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. |
| 57 | void time_sleep(time_delta dt); | 60 | void time_sleep(time_delta dt); |
| 58 | 61 | ||
