-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain_loop.lua
More file actions
198 lines (175 loc) · 4.97 KB
/
Copy pathmain_loop.lua
File metadata and controls
198 lines (175 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
local path = (...):gsub(".main_loop", "")
local frequency_counter = require(path..".frequency_counter")
local main_loop = class({
name = "main_loop",
})
function main_loop:profiler_push(f)
if self.profiler then
self.profiler:push(f)
end
end
function main_loop:profiler_pop(f)
if self.profiler then
self.profiler:pop(f)
end
end
function main_loop:new(args)
self.frametime = args.frametime or 1 / 60
if self.interpolate_render == nil then
self.interpolate_render = false
end
self.ticks_per_second = frequency_counter()
self.frames_per_second = frequency_counter()
self.interpolate_render = args.interpolate_render
--time scaling
self.timescale = args.timescale or 1
self.time = 0
self.ticktime = 0
self.garbage_time = args.garbage_time or 1e-3
self.after_frame = args.after_frame
self.profiler = args.profiler
self.input = args.input
--redefine main loop
function love.run()
if love.load then
love.load(love.arg.parseGameArguments(arg), arg)
end
--(dont count love.load time)
love.timer.step()
--accumulator
local frametimer = 0
-- Main loop time.
return function()
local start_of_loop = love.timer.getTime()
self:profiler_push("frame")
self:profiler_push("active")
self:profiler_push("event")
-- process and handle events
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
self:profiler_pop("event")
-- get time passed, and accumulate
self:profiler_push("update")
local raw_dt = love.timer.step()
local dt = raw_dt * self.timescale
self.time = self.time + dt
-- fuzzy timing snapping
for _, v in ipairs {0.5, 1, 2} do
v = self.frametime * v
if math.abs(dt - v) < 0.002 then
dt = v
end
end
-- dt clamping
dt = math.clamp(dt, 0, 2 * self.frametime)
frametimer = frametimer + dt
-- accumulater clamping
frametimer = math.clamp(frametimer, 0, 8 * self.frametime)
local ticked = false
--spin updates if we're ready
while frametimer > self.frametime do
self:profiler_push("tick")
self.ticktime = self.ticktime + self.frametime
frametimer = frametimer - self.frametime
love.update(self.frametime) --pass consistent dt
self.ticks_per_second:add()
ticked = true
self:profiler_pop("tick")
end
self:profiler_pop("update")
--render if we need to
self:profiler_push("draw")
if
love.graphics
and love.graphics.isActive()
and (ticked or self.interpolate_render)
then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
love.draw(frametimer / self.frametime) --pass interpolant
if not (self.profiler and love.keyboard.isDown("`")) then
love.graphics.present()
end
self.frames_per_second:add()
end
self:profiler_pop("draw")
self:profiler_pop("active")
if self.after_frame then
self:after_frame()
else
--sweep garbage always
self:profiler_push("collect")
manual_gc(self.garbage_time)
self:profiler_pop("collect")
--give the cpu a break proportionate to how much spare time we have
local time_until_now = love.timer.getTime() - start_of_loop
local sleep_f = math.clamp01((time_until_now * 1.2) / self.frametime)
local sleep_time = math.lerp(self.frametime / 2, 0, sleep_f)
if sleep_time > 0 then
self:profiler_push("sleep")
love.timer.sleep(sleep_time)
self:profiler_pop("sleep")
end
end
self:profiler_pop("frame")
--profiler stuff if relevant
if self.profiler then
if love.keyboard.isDown(";") then
if not self._pressed_write then
love.filesystem.write("profile.txt", self.profiler:format())
self._pressed_write = true
end
else
self._pressed_write = false
end
if love.keyboard.isDown("`") then
--special key handling
if love.keyboard.isDown("lctrl") then
if not self._pressed_special then
self.profiler:clear_worst()
self._pressed_special = true
end
elseif love.keyboard.isDown("return") then
if not self._pressed_special then
self.profiler:print_result()
self._pressed_special = true
end
elseif love.keyboard.isDown("lshift") then
if not self._pressed_special then
if not self.profiler._hold then
self.profiler:hold_result()
end
self._pressed_special = true
end
else
if self.profiler._hold then
self.profiler:drop_hold()
end
--released
self._pressed_special = false
end
love.graphics.push()
love.graphics.origin()
love.graphics.translate(10, 10)
if not self.profiler_font then
self.profiler_font = love.graphics.newFont(10)
end
love.graphics.setFont(self.profiler_font)
self.profiler:draw_result()
love.graphics.pop()
love.graphics.present()
end
end
end
end
end
return main_loop