Skip to content

Commit 79c8aa7

Browse files
committed
Unroll fixed stepDelta time updates
1 parent 4409c06 commit 79c8aa7

File tree

2 files changed

+54
-46
lines changed

2 files changed

+54
-46
lines changed

Sources/GateEngine/Game.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,37 @@ public final class Game {
106106

107107
#if GATEENGINE_PLATFORM_EVENT_DRIVEN
108108
@MainActor internal func eventLoop(completion: @escaping () -> Void) {
109-
guard let highPrecisionDeltaTime = deltaTimeHelper.getDeltaTime() else {
109+
guard let fixedStepDeltaTime = deltaTimeHelper.getFixedStepDeltaTime() else {
110110
completion()
111111
return
112112
}
113113

114114
// Add a high priority Task so we can jump the line if other Tasks were started
115115
Task(priority: .high) { @MainActor in
116-
let deltaTime = Float(highPrecisionDeltaTime)
117-
self.resourceManager.update(withTimePassed: deltaTime)
118-
await windowManager.updateWindows(deltaTime: deltaTime)
119-
self.windowManager.drawWindows()
120-
if await self.ecs.shouldRenderAfterUpdate(
121-
withTimePassed: Float(deltaTime)
122-
) {
116+
var shouldRender = false
117+
for _ in 0 ..< fixedStepDeltaTime.steps {
118+
let deltaTime = Float(fixedStepDeltaTime.deltaTime)
119+
self.resourceManager.update(withTimePassed: deltaTime)
120+
await windowManager.updateWindows(deltaTime: deltaTime)
121+
if await self.ecs.shouldRenderAfterUpdate(withTimePassed: deltaTime) {
122+
shouldRender = true
123+
}else{
124+
break
125+
}
126+
}
127+
if shouldRender {
123128
self.windowManager.drawWindows()
124-
completion()
125-
} else {
129+
}else{
126130
#if GATEENGINE_DEBUG_RENDERING
127131
Log.warn("Frame Dropped", "DeltaTime:", deltaTime)
128132
#endif
129-
completion()
130133
}
134+
completion()
131135
}
132136
}
133137
#else
134138
internal func gameLoop() {
139+
#error("Needs to rewritten to match EventDriven!")
135140
guard let deltaTime = getNextDeltaTime() else {
136141
Task(priority: .high) { @MainActor in
137142
self.gameLoop()

Sources/GateEngine/Helpers/DeltaTimeHelper.swift

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,81 +5,84 @@
55
* http://stregasgate.com
66
*/
77

8+
/// Assits in creating fixed step deltaTime values
89
internal struct DeltaTimeHelper {
910
internal let name: String
10-
private var accumulator: Double = 0
1111
private var previousTime: Double = .nan
1212

1313
private static let preferredFrameRate: Int = Platform.current.prefferedFrameRate()
14-
private static let simulationStepDuration: Double = 1 / Double(preferredFrameRate) / 2
14+
private static let simulationStepDuration: Double = 1 / Double(preferredFrameRate) / 4.10
1515
private static let maxSimulationSteps: Int = 4
1616

1717
internal init(name: String) {
1818
self.name = name
1919
self.reset()
2020
}
2121

22-
mutating func reset() {
23-
self.accumulator = 0
22+
/**
23+
Revert to initial state.
24+
25+
`DeltaTimeHelper` stores time to compute deltaTime.
26+
Use this function to clear the stored time when it is known to be stale.
27+
*/
28+
@inlinable mutating func reset() {
2429
self.previousTime = .nan
2530
}
2631

32+
/**
33+
Computes a fixed step deltaTime based on the current platfo
34+
- returns A fixed step deltaTime, or nil if the number of steps was zero.
35+
*/
2736
@MainActor
28-
@inlinable
29-
mutating func getDeltaTime() -> Double? {
37+
@inlinable mutating func getFixedStepDeltaTime() -> (steps: Int, deltaTime: Double)? {
3038
let currentTime = Platform.current.systemTime()
3139

3240
// Determine the new accumulator value
33-
let newAccumulator: Double = self.accumulator + (currentTime - self.previousTime)
41+
let literalDeltaTime: Double = currentTime - self.previousTime
3442

3543
// If our accumulated time is less than a single step, bail
36-
if newAccumulator < Self.simulationStepDuration {
44+
if literalDeltaTime < Self.simulationStepDuration {
3745
return nil
3846
}
3947

4048
// Update previousTime if we get this far
4149
self.previousTime = currentTime
4250

4351
// If our accumulated time is not a number, bail
44-
if newAccumulator.isFinite == false {
52+
if literalDeltaTime.isFinite == false {
4553
return nil
4654
}
4755

48-
// Update the accumulator
49-
self.accumulator = newAccumulator
50-
5156
// The number of steps that currently fit in the accumulator
52-
let stepsRequired = Int(accumulator / Self.simulationStepDuration)
57+
let stepsRequired = Int(literalDeltaTime / Self.simulationStepDuration)
5358

54-
// If stepsRequired is greater than maxSimulationSteps, return only that
55-
// Erase the accumulator as this is likely a spike from a CPU hang
59+
// If stepsRequired is greater than maxSimulationSteps, then truncate.
5660
if stepsRequired > Self.maxSimulationSteps {
57-
// Anytime we return from here, the simulation will run slower.
61+
#if !DEBUG && !DISTRIBUTE
62+
// Anytime we return from here, the simulation is running slower.
5863
// This is similar in severity to a dropped frame and will happen if the simulation is lagging.
5964
// To reduce occurances of this, do less work.
60-
// Try spreading complex tasks out across multiple updates.
61-
let message = "\(name) deltaTime attempted to use \(stepsRequired) steps, and was truncated to \(Self.maxSimulationSteps)."
62-
if stepsRequired > 15 {
65+
// Try spreading work out across multiple updates.
66+
let message = "\(name) needed \(stepsRequired) steps, and was truncated to \(Self.maxSimulationSteps)."
67+
if stepsRequired > Self.maxSimulationSteps * 2 {
6368
Log.warn(message, "This is a \(String(format: "%0.3f", Self.simulationStepDuration * Double(stepsRequired))) second hang!")
64-
}else{
65-
Log.debug(message, "This is a minor performance issue.")
6669
}
67-
68-
let maxSimulationSteps = Double(Self.maxSimulationSteps)
69-
70-
// Zero the accumulator to give the next update the maximum time, becuase we just had a hang
71-
self.accumulator = 0
70+
#endif
7271

7372
// Return the maximum allowed deltaTime
74-
return Self.simulationStepDuration * maxSimulationSteps
73+
return (Self.maxSimulationSteps, Self.simulationStepDuration)
7574
}
76-
77-
// Calculate fixed step deltaTime
78-
let deltaTime = Self.simulationStepDuration * Double(stepsRequired)
79-
80-
// Remove the used time from the accumulator
81-
self.accumulator -= deltaTime
82-
83-
return deltaTime
75+
76+
return (stepsRequired, Self.simulationStepDuration)
77+
}
78+
79+
/**
80+
Computes a fixed step deltaTime based on the current platfo
81+
- returns A fixed step deltaTime, or nil if the number of steps was zero.
82+
*/
83+
@MainActor
84+
@inlinable mutating func getDeltaTime() -> Double? {
85+
guard let fixedStepDeltaTime = self.getFixedStepDeltaTime() else {return nil}
86+
return fixedStepDeltaTime.deltaTime * Double(fixedStepDeltaTime.steps)
8487
}
8588
}

0 commit comments

Comments
 (0)