Just to humor you, I grabbed my old Macbook from 2009 (specs, running 10.11.6 (El Capitan)), and your attached .love only consumed 8% CPU at startup, and 5.6~5.8% CPU after a little while.
Maybe you have some power saving settings on, and GPU is not properly firing up? Dunno, at this point it might sound like your Macbook Pro specifically is at fault somehow.
Love CPU usage
- slime
- Solid Snayke
- Posts: 3161
- Joined: Mon Aug 23, 2010 6:45 am
- Location: Nova Scotia, Canada
- Contact:
Re: Love CPU usage
There was a bug in the version of OpenAL Soft shipped with the original love 11.3 download for macOS which caused increased CPU usage on a separate thread. A fixed version of love for macOS was reuploaded a while ago which doesn't have that issue - the fixed version is labelled "11.3b" in its version description. It's worth checking to make sure you're using that newer 11.3 version, I think.ChicoGameDev wrote: ↑Sat Aug 29, 2020 7:29 pm I'm currently on a Macbook Pro and even an empty application with vsync off and just the sleep the CPU is up to ~103% here is the love file for testing purpose.
- ChicoGameDev
- Citizen
- Posts: 70
- Joined: Thu Feb 14, 2019 6:02 pm
- Location: Switzerland
- Contact:
Re: Love CPU usage
Oh yeah
Thank you so much Slime you are amazing !
That was it I downloaded again and I'm now at 8% CPU usage. That's truly amazing ! Glad I asked
Thanks everybody !
Regards,
Thank you so much Slime you are amazing !
That was it I downloaded again and I'm now at 8% CPU usage. That's truly amazing ! Glad I asked
Thanks everybody !
Regards,
Re: Love CPU usage
As a side note, I prefer avoiding modification of love.run because it tends to change a lot between major versions, so when the version changes it can be a pain to update, and a major pain to keep the program working with both versions at a time.
But then a frame limiter should best be implemented at the end of love.draw, not on love.update.
But then a frame limiter should best be implemented at the end of love.draw, not on love.update.
- ChicoGameDev
- Citizen
- Posts: 70
- Joined: Thu Feb 14, 2019 6:02 pm
- Location: Switzerland
- Contact:
Re: Love CPU usage
That's true.
Originally it was in the love.draw but when I was reading it again for making this post I thought I've done something wrong .
Thanks for confirming and I've just put it back into the love.draw.
Regards
Originally it was in the love.draw but when I was reading it again for making this post I thought I've done something wrong .
Thanks for confirming and I've just put it back into the love.draw.
Regards
Re: Love CPU usage
If you can guarantee that you won't overwrite or nillify love.draw or love.update, then you don't need to keep checking their presence at every cycle. These table lookups should be pretty fast already, but if you want to be pedantic you can cache them.
This is a love.run() for v11.3:
That infinite 'while' loop is based on the fact that the boot code keeps running the function returned by love.run() until that function returns a non-nil value, as seen here: https://github.com/love2d/love/blob/mas ... #L801-L805
This is a love.run() for v11.3:
Code: Select all
local FRAME_TIME = 1.0 / 30.0 -- 30.0 = desired FPS
-- Custom love.run with frame limiting.
function love.run()
if not love.timer or not love.event or not love.graphics then
error('Modules not enabled')
end
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
-- Main loop.
return function()
local timerStep = love.timer.step
local timerSleep = love.timer.sleep
local eventPump = love.event.pump
local eventPoll = love.event.poll
local eventHandlers = love.handlers
local graphicsActive = love.graphics.isActive
local graphicsPresent = love.graphics.present
local loveDraw = love.draw
local loveUpdate = love.update
local dt1 = 0.0
local dt2 = 0.0
local sleepTime = 0.0
while true do
eventPump()
for name, a,b,c,d,e,f in eventPoll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
eventHandlers[name](a,b,c,d,e,f)
end
-- Call update and draw.
dt1 = timerStep()
loveUpdate(dt1+dt2)
if graphicsActive() then
loveDraw()
graphicsPresent()
end
dt2 = timerStep()
sleepTime = FRAME_TIME - (dt1+dt2)
if sleepTime > 0.0 then
timerSleep(sleepTime)
end
end
end
end
Re: Love CPU usage
If you want your program to be compatible with love.js, the function returned by love.run should only process one frame and return, as the docs say.
Re: Love CPU usage
Thats important to know, thank you for the pointer. I'm personally only interested in the desktop and Android platforms.
I searched and the discussion behind changing love.run() from a loop to a function is in here, pretty interesting:
- https://github.com/love2d/love/issues/1273
- https://github.com/love2d/love/commit/7 ... 7aa4b4fbf4
This wikipedia article says Android uses preemptive multitasking like the desktop platforms, so hopefully it should be ok with a loop-based love.run() main loop.
I searched and the discussion behind changing love.run() from a loop to a function is in here, pretty interesting:
- https://github.com/love2d/love/issues/1273
- https://github.com/love2d/love/commit/7 ... 7aa4b4fbf4
This wikipedia article says Android uses preemptive multitasking like the desktop platforms, so hopefully it should be ok with a loop-based love.run() main loop.
Re: Love CPU usage
Apologies in advance for drifting the topic even further. I've been examining your main loop in more detail.
That order does not make much sense to me. At the very least, the event callbacks should be included in the measurement; there's no reason to exclude them. Then, it only works with vsync disabled; therefore it can't be used as a frame rate limiter [edit: oops, I was wrong there, sorry]. The actual frame rate will also be variable, not fixed, as the time when the frame is displayed (graphicsPresent) depends on the duration of the update and draw callbacks, even though the average frame time will be correct (assuming the input events take negligible time, or that the first time measurement is taken before them).
The correct order in my opinion is this:
The only problem is that it can possibly introduce a little more latency, but now it can be used both for frame rate limiting and to produce a truly fixed frame rate, depending on vsync.
Anyway, you get the same effect with the code that Chicco posted, assuming it's at the end of love.draw.
This is the order in which that code does things:RNavega wrote: ↑Tue Sep 01, 2020 6:21 amCode: Select all
while true do eventPump() for name, a,b,c,d,e,f in eventPoll() do if name == "quit" then if not love.quit or not love.quit() then return a or 0 end end eventHandlers[name](a,b,c,d,e,f) end -- Call update and draw. dt1 = timerStep() loveUpdate(dt1+dt2) if graphicsActive() then loveDraw() graphicsPresent() end dt2 = timerStep() sleepTime = FRAME_TIME - (dt1+dt2) if sleepTime > 0.0 then timerSleep(sleepTime) end end end
Code: Select all
|--------------------------------------------------------|
| | | | | |
process measure process process display sleep
event time update draw frame (*)
callbacks callback callback
(*) sleeps {target frame time - (update + draw + display)}
The correct order in my opinion is this:
Code: Select all
|------------------------------------------------------|
| | | | | |
measure process process process sleep display
time event update draw (*) frame
callbacks callback callback
(*) sleeps {target frame time - (input + update + draw + display)}
Anyway, you get the same effect with the code that Chicco posted, assuming it's at the end of love.draw.
Re: Love CPU usage
The goal is getting a main loop that's as generous as possible to the CPU so you have low usage/no busy-spinning, it should do a game tick and then sleep to give all the remaining time back to the OS.
The loop I posted I based on the first one in the deWiTTERS game loop article. To get my head around it I made this diagram:
(With "m" standing for "measure time"/love.timer.step(), and "s?" standing for "sleep if you have time to spare")
One of the love.timer.step() calls (m1) needs to go right before the love.update() callback, because 'dt' must be guaranteed to be the time since the last call to it.
This loop has a vulnerability: if the system can't keep up with the desired framerate because the game is too heavy, then the game will slow down. It only caps/limits the framerate if it's too fast, it doesn't skip frames if it's falling behind.
However, that's okay to me, all of the LÖVE games I tested are very lightweight and don't go above 10% CPU use on my low-end machine.
My thinking for putting the sleep after love.graphics.present() is that, if you need to sleep at all, it should happen right after you've just swapped buffers and have new content on the screen, because present() might stall a bit if using VSync and you want love.timer.step() to include that time.
But after what you said, I looked in the LÖVE source and noticed that the vsync setting is sent to SDL as an integer (same meaning as this). If you want to cap the framerate at half the refresh rate (30 FPS on a 60hz monitor, or 72 FPS on a 144Hz monitor) you're allowed to put a...
...in conf.lua. The CPU usage reduces a lot and the framerate it produces is more consistent than when manually sleeping like I was doing. The mainloop reduces to:
So VSync = 2 might be the default in your low-CPU game, and if the user wants a faster rate then they can lower that from 2 to 1 or to 0 on your options screen or whatever interface you have for that, with 2 being "FPS = half refresh rate", 1 being "FPS = refresh rate" and 0 being "unrestricted upates, no waiting" (love.graphics.present() returns immediately rather than waiting).
In all cases you rely on love.update's 'dt' to advance the speeds-per-second of your game.
The loop I posted I based on the first one in the deWiTTERS game loop article. To get my head around it I made this diagram:
(With "m" standing for "measure time"/love.timer.step(), and "s?" standing for "sleep if you have time to spare")
One of the love.timer.step() calls (m1) needs to go right before the love.update() callback, because 'dt' must be guaranteed to be the time since the last call to it.
This loop has a vulnerability: if the system can't keep up with the desired framerate because the game is too heavy, then the game will slow down. It only caps/limits the framerate if it's too fast, it doesn't skip frames if it's falling behind.
However, that's okay to me, all of the LÖVE games I tested are very lightweight and don't go above 10% CPU use on my low-end machine.
My thinking for putting the sleep after love.graphics.present() is that, if you need to sleep at all, it should happen right after you've just swapped buffers and have new content on the screen, because present() might stall a bit if using VSync and you want love.timer.step() to include that time.
But after what you said, I looked in the LÖVE source and noticed that the vsync setting is sent to SDL as an integer (same meaning as this). If you want to cap the framerate at half the refresh rate (30 FPS on a 60hz monitor, or 72 FPS on a 144Hz monitor) you're allowed to put a...
Code: Select all
t.window.vsync = 2
Code: Select all
-- Main loop.
return function()
local timerStep = love.timer.step
local timerSleep = love.timer.sleep
local eventPump = love.event.pump
local eventPoll = love.event.poll
local eventHandlers = love.handlers
local loveUpdate = love.update
local loveDraw = love.draw
local graphicsPresent = love.graphics.present
timerStep()
while true do
eventPump()
for name, a,b,c,d,e,f in eventPoll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
eventHandlers[name](a,b,c,d,e,f)
end
-- Call update and draw.
loveUpdate(timerStep())
loveDraw()
-- Testing love.graphics.isActive() is only needed on mobile platforms (iOS/Android)
-- or if you call love.window.close() while the program is still running.
graphicsPresent()
end
end
In all cases you rely on love.update's 'dt' to advance the speeds-per-second of your game.
Who is online
Users browsing this forum: No registered users and 2 guests